On 3 September 2026 the Next.js blog TTFB on pristren.com was about 345 ms in production. All 550 posts rendered on every request. The dynamic post route had no generateStaticParams, so a revalidate setting had no cache to attach to.
What the Next.js blog TTFB measurement covered
The check was the public site, not a local build. These figures are the production ones:
| Signal | Value |
|---|---|
| Published posts | 550 |
| Posts in the sitemap | 542 |
| Monthly users | about 10,000 to 12,000 |
| Users per post per month | about 18 to 22 |
| Cache on the blog routes | no-store |
| Live TTFB | about 345 ms |
| First Load JS | about 256 KB |
| Canonical tags on a page | 2, and the first pointed at the homepage |
Monthly users are the owner's range for the site. Users per post is that range divided by 550 posts. Neither number is a Search Console click export.
The same production rows ship as JSON at public/data/blog-cache-measurements-2026-09-03.json. That file is on the next deploy of this site. Until then, the table in this post is the source to cite.
Why the cache never attached
Two separate facts produced the uncached behavior.
The post route is a dynamic segment. Without generateStaticParams, Next did not create an incremental static regeneration bucket for those URLs. The ISR guide describes the same requirement. The route has to be statically generated before a revalidate window means anything.
The page also reads MongoDB directly. It does not go through fetch(). A revalidate export of 3600 seconds only applies to the Data Cache on fetch(). On this route it did nothing. Every one of the 550 posts paid a server render. TTFB was the time that render took, about 345 ms on the live host.
The listing page had a second opt-out. It read searchParams, which in this Next version forces the route to stay dynamic. Search and sort now live on their own URLs so the index can stay static.
One choice we left in place: next/image stays off. The image library is a dev dependency and is pruned in production, and on-demand transcoding on a 2 vCPU host would re-encode hundreds of covers after each deploy. Covers use Cloudinary width, format, and quality parameters instead.
There is also no shared cache handler across servers. All of the posts are baked into the build, and only one replica is running. A shared handler becomes worth it at two replicas, not before.
What changed on the production route
generateStaticParams now covers the published posts. dynamicParams stays on, so a new post can still render before the next build. Category hubs and the pagination routes are prerendered the same way.
A second defect sat next to the cache bug. The root layout hardcoded a homepage canonical, so every page shipped two canonical tags and the homepage one came first. That is now one canonical per page. Conflicting canonicals decide which URL Google keeps. They do not add referring domains.
This note does not publish timings from a local build. That environment is not linked to production, so a local cache hit is not a production result.
Related reading on this site: the Next.js 15 guide and the API route patterns. The same engineering shows up in the project write-ups.
What this does not claim
These numbers do not raise Domain Authority. That score follows referring domains, which means other sites that link here. A faster blog earns those links only when someone cites the measurement. The table above is the part worth citing.
There is no production TTFB from after the deploy in this dataset. There is no claim about ranking movement.
How to repeat the check
Request a live post on pristren.com and read the cache headers. Count canonical tags and confirm the only one matches the post URL. Compare First Load JS in the browser network panel with the 256 KB mark above. Playwright is a reasonable way to lock the header check into CI.
If the cache header is missing, check generateStaticParams before tuning the database. The 345 ms was the cost of rendering the page on the request, not a slow query on its own.

Comments
Have a question or something to add? Sign in to join the discussion.