"The page is prerendered" is a statement about the build. It does not say what happens when a request arrives: whether an edge cache has a copy, whether the app runtime serves one, or whether the page is rendered again for every visitor. Those are deployment questions, and they show up in one number — time to first byte — and one header, the cache status.
This article measures both on this site, explains why a prerendered page can still be rendered on every request, and lays out the trade between correctness and TTFB. It also says plainly that TTFB is not a citation signal.
What the numbers mean
- TTFB — time from the request to the first byte of the response. On a server-rendered page it includes routing, data, rendering and network; on a cached page it is mostly network.
- Total time — TTFB plus the transfer. For a small HTML page the gap is usually the network, not the server.
x-nextjs-cache— the framework’s own cache status.HITmeans a stored render was served;MISSmeans it was rendered.cf-cache-status/Age— the CDN’s view: whether the edge held the response and for how long.
A measurement on this site
On 2026-10-29 I requested four URLs three times each from one network and recorded TTFB and total time. The numbers are network-bound — a reader in another region will see different absolutes — but the shape is the point.
| URL | TTFB (s, 3 runs) | Total (s, 3 runs) |
|---|---|---|
/ | 1.99, 2.40, 1.54 | 3.36, 4.42, 2.64 |
/blog/cache-headers-what-an-auditor-sees/ | 1.96, 0.75, 1.63 | 2.80, 5.31, 2.63 |
/pricing/ | 3.79, 1.98, 1.15 | 5.03, 2.84, 1.88 |
/report/?domain=example.com | 1.32, 2.70, 0.89 | 1.73, 3.04, 1.40 |
TTFB ranges from roughly 0.75 to 3.8 seconds, and the same URL varies by more than a second between runs. The headers explain part of it: the homepage returns cache-control: public, max-age=0, must-revalidate and x-nextjs-cache: MISS on repeated requests, and the response carries no cf-cache-status or Age. In other words, the edge is not holding the HTML, and the route is rendering rather than serving a stored copy.
Why a prerendered page still renders
There are four separate reasons, and they stack:
- Prerendering is a build artifact, not a cache. The HTML exists on disk, but something has to serve it. A Worker response is not automatically cached by the CDN; Cloudflare’s documentation says Worker responses are not cached by default.
- The header forbids reuse without revalidation. After the 2026-10-23 incident, HTML is
max-age=0, must-revalidate. Every cache must check with the origin before serving; a full HTML response is the cost of that check when no ETag path is available. - The framework cache may be per-isolate.
x-nextjs-cache: MISSmeans the render was not served from the framework’s incremental cache. Without a durable cache behind it, an isolate restart loses it, and the next request renders again. - Some pages are intentionally dynamic.
/report/and/api/areno-storebecause they depend on the request; their TTFB is a different question.
The second reason is a deliberate choice and the first and third are deployment properties. That distinction matters: a slow TTFB on this site is not evidence that prerendering failed. It is evidence that the deployment chose correctness over edge caching, and that the durable cache is not doing what a static host would do for free.
The options, and what each costs
| Option | Effect | Cost / risk |
|---|---|---|
| Accept revalidation | Every HTML view checks with the origin; correctness is preserved. | A round trip and a render when there is no usable ETag; slower TTFB. |
| Edge-cache HTML with a short TTL and purge on deploy | Most requests are served at the edge. | A staleness window after deploy if the purge misses; needs a cache rule that respects the decision. |
| Durable incremental cache + on-demand revalidation | Store renders across isolates, invalidate on content change. | More moving parts: a cache binding, a revalidation path, a failure mode when the store is unreachable. |
| Paid Workers CPU | Removes the cold-start CPU ceiling. | A monthly cost; does not change the render-per-request logic. |
This site has chosen the first option and paid for it in TTFB. The choice is defensible for a product whose claim is that the published version is the running version; it would be the wrong choice for a marketing site whose pages change monthly and whose readers are far from the origin.
How to measure your own
# 1. TTFB and total, three times, for one URL
for i in 1 2 3; do
curl -sS -o /dev/null -w "%{time_starttransfer} %{time_total} %{http_code}\n" \
https://your-domain.com/page/
done
# 2. What does the response say about caching?
curl -sS -D - -o /dev/null https://your-domain.com/page/ \
| grep -iE 'cache-control|age|etag|cf-cache-status|x-nextjs-cache'
# 3. The same page, fetched from a cache-busting hostname or region if available
# (a different network is the cheapest approximation)The second command is the one that turns a raw number into an explanation. A slow TTFB with x-nextjs-cache: HIT is a network problem; a slow TTFB with MISS and no edge status is a render problem. They have different fixes.
What TTFB is not
It is not a GEO score. No engine has published a TTFB threshold for citation, and a fast page with no extractable content is not more citable than a slower one that answers the question. It is a delivery dimension: it affects readers and can affect whether a crawler waits for the response at all.
The honest summary is the one the numbers support: prerendered does not mean cached, and "the page exists at build time" does not answer "what did the request receive". Measure the response, name the trade, and do not let a build-time claim stand in for a runtime fact.