August 22, 2026 · 2 min read
One line in the layout made the site three times slower
The site answered in about 880 ms. The first explanation was geographic: the server is far away, packets take time. Moving it closer gave 679 ms — noticeably better, and nowhere near what we expected.
So it was not distance. The next step was to look at what actually happens on every request.
The shared layout asked for the session: who is signed in, so the header can show a name. One line, looks harmless. But reading the session means reading a cookie, and reading a cookie makes the page dynamic: Next stops treating it as static, stops serving it from cache and rebuilds it on every request. The layout serves every page, so the whole site fell under that rule.
The fix is not to ask for the session on the server. The header finds out who is signed in from the browser, with its own request. Pages return to the cache and are served from the edge node nearest the visitor.
Response time dropped to roughly 300 ms.
There is a visible price: while the browser is finding out who is signed in, a blank space of the same width holds the place of the sign-in button. Without it the header would jump on every load.
The lesson worth keeping: in Next, any access to a cookie, a header, or anything else request-dependent does not cost milliseconds per call — it costs the caching of every page under that layout. Such calls are more expensive than they look, and most expensive of all in shared layouts.
This site now has a standing rule because of it: read nothing request-dependent in a layout.
export default async function LocaleLayout({ children, params }) { const { locale } = await params; /** * Сессию здесь НЕ спрашиваем. * * auth() читает куку, а это делает страницу динамической: Next * пересобирает её на каждый запрос и запрещает кэширование на * пограничных узлах. Из-за одной строки весь сайт ехал целиком * при каждом открытии. * * Теперь сессию получает шапка на стороне браузера — см. * SessionBoundary. Страница остаётся статической. */ const settings = await getSettings(); return <SessionBoundary>{children}</SessionBoundary>;}