Opening a post here used to take close to ten seconds. The content is eight files that only change when someone commits, and every single visit was rendering them from scratch on a serverless function.
The build output said so plainly: ƒ / and ƒ /[slug] — every route dynamic.
Responses came back private, no-cache, no-store with a CDN miss, so no post
was ever served twice from cache.
The cause was one line. supabase.auth.getUser() in the root layout, reading
cookies, which makes the entire tree below it dynamic. It was there to print an
email address in the header.
Warm requests measured 250–350ms, so the ten seconds was cold start: a function bundle carrying React, Supabase, the MDX runtime and Shiki's grammars. A low-traffic blog is almost always cold, which means most real visits paid the worst number rather than the median.
The session read is gone, along with a second getUser() call in the proxy
that fired on every prefetch. The blog no longer knows who you are, and does not
need to — nothing on it is gated.
To stop it drifting back, every route now declares force-static, and the
build fails if any route goes dynamic again. A performance fix that can silently
regress is a performance fix with a timer on it.