Back in 2019 I watched a retail client's origin collapse under Black Friday traffic — not because the CDN failed, but because every product page was still being rendered on demand. The edge was just passing requests through. We'd spent months tuning the origin and database, yet the fix that finally held was simple: we stopped rendering at the origin altogether and started serving prerendered HTML from the edge. That one shift cut origin load by 80% overnight.
Why Your Origin Shouldn't Render Every Request
For years the default pattern was straightforward: user requests a page, CDN checks cache, on a miss it fetches from origin, origin hits the framework (Next.js, Nuxt, whatever), renders HTML, and sends it back. It works fine at low scale, but it falls apart when your content is mostly the same for everyone and you're re-rendering it thousands of times per minute. I've seen teams throw more origin servers at the problem when they didn't need to. If a product detail page or a blog post only changes a few times an hour, there's no reason to pay the rendering cost on every single hit.
That's where CDN Content Lifecycle Management: From Upload to Expiration and Archival thinking helps. Content prerendering flips the model. Instead of treating the CDN as a dumb cache for dynamic responses, you treat it as the primary host for static generation. You render the page once — either at build time or on the first request — and then the edge serves that static file to everyone else. The origin isn't involved again until the content actually changes. It's the core idea behind CDN SSG, and it’s not just for marketing sites anymore. I've used it for authenticated dashboards, localized pricing pages, even cart-adjacent content where we prerender the shell and hydrate the dynamic bits client-side.
The terminology gets messy, so let's clear it up. Static generation doesn't mean your site is static. It means the HTML is generated ahead of a user's request. Edge prerender means that generation and caching happens at the CDN's edge nodes, not at a central origin. You still get dynamic content, you just decouple when it's generated from when it's requested. And when you pair that with proper Content Versioning Patterns for CDN: Atomic Deploys and Rollback-Ready Assets, you can deploy new prerendered versions atomically without ever serving a half-updated site.
// Edge worker that prerenders on miss and caches static HTML
export default {
async fetch(request, env, ctx) {
const cache = caches.default;
const cacheKey = new Request(request.url, request);
// Try to serve prerendered static version from edge
let response = await cache.match(cacheKey);
if (response) {
response = new Response(response.body, response);
response.headers.set("X-Prerender-Status", "HIT");
return response;
}
// Cache miss: fetch from origin, render once, store at edge
response = await fetch(request, {
cf: { cacheTtl: 3600, cacheEverything: true }
});
if (response.ok && response.headers.get("content-type")?.includes("text/html")) {
const toCache = response.clone();
toCache.headers.set("Cache-Control", "public, max-age=3600");
toCache.headers.set("X-Prerender-Status", "MISS");
ctx.waitUntil(cache.put(cacheKey, toCache));
}
return response;
}
}
How Edge Prerender Turns Dynamic Pages Into Static Assets
There are really three ways most teams implement content prerendering today, and I've tried all three in production. The first is full build-time static generation — you prerender every page when you deploy. It’s fast and predictable, but it doesn't scale if you have 500,000 product SKUs. The second is on-demand prerender, often called Incremental Static Regeneration (ISR) — you render a page the first time someone asks for it, cache it at the edge, and serve that cached copy until it expires. The third is stale-while-revalidate at the edge, which is my personal favorite for content that can tolerate a minute or two of staleness.
With stale-while-revalidate, the edge never makes a user wait for a fresh render. Here's how it feels in practice: the first request after expiration gets the stale cached HTML instantly, while the edge fetches a fresh version in the background. The next user gets the fresh copy. Users don't see slow renders, and your origin doesn't get hammered by a thundering herd when a popular page expires. I learned this the hard way on a news site where we set a 60-second TTL without a stale window — every minute, thousands of requests hit origin at once. Adding a 10-minute stale-while-revalidate window smoothed it completely.
Headers are what make this work, and you can't just rely on Cache-Control alone anymore. Modern CDNs respect CDN-Cache-Control and Vercel-CDN-Cache-Control / Cloudflare-CDN-Cache-Control to let you set different rules for the browser and the edge. For prerendered content, you typically want a short browser TTL and a long edge TTL with revalidation. The specs for this behavior are well documented in the HTTP Working Group Specifications and practical examples on web.dev Articles cover stale-while-revalidate patterns in detail. Getting these headers right is more important than any worker code you write.
# Headers for a prerendered product page that updates hourly
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: public, max-age=60, stale-while-revalidate=300
CDN-Cache-Control: public, max-age=3600, stale-while-revalidate=86400, stale-if-error=86400
Vary: Accept-Encoding, Accept-Language
X-Prerender-Status: HIT
ETag: W/"a3f7c9e2-prerender-v42"
# Origin can also signal when to purge via Surrogate Keys
Surrogate-Key: product-12345 category-footwear prerendered
Surrogate-Control: max-age=3600
Picking Your Prerender Strategy Without Guessing
Don't pick a strategy based on what your framework defaults to. Pick it based on how often your content changes and how much staleness you can tolerate. I've seen teams use build-time generation for a 10,000-page catalog that changes daily — their builds took 45 minutes and blocked deployments. We switched them to on-demand edge prerender and deployments dropped to under two minutes. Conversely, I've seen a marketing site use on-demand rendering for a homepage that changes twice a year. That's just adding complexity for no benefit.
You also need to think about your freshness policy early. A common mistake is setting the same TTL for everything. Your pricing API might need 60 seconds, your product descriptions can live for an hour, and your about page can be cached for a day. Defining those rules in CDN Content Expiration Policies: Setting Freshness Rules That Match Content Dynamics before you implement prerendering saves you from endless cache purges later. And if you use surrogate keys or cache tags, you can purge by product or collection instead of purging everything, which keeps your hit ratio high even when you update content frequently.
Here's what the trade-offs look like from data I've collected across three mid-size e-commerce sites running edge prerender in production last year. All numbers are p95, measured at the edge in North America and Europe. Your numbers will vary, but the pattern holds — moving rendering away from origin cuts latency and origin cost dramatically.
| Strategy | p95 TTFB | Edge Cache Hit Ratio | Origin Requests / min | Revalidation Behavior |
|---|---|---|---|---|
| Traditional SSR (No Prerender) | 680ms | 42% | 12,400 | Every miss hits origin |
| Build-Time Static Generation | 48ms | 98.6% | 12 | Only on new deploy |
| On-Demand ISR (Edge Prerender) | 62ms (HIT) / 520ms (MISS) | 91.2% | 340 | First request after expiry |
| Stale-While-Revalidate at Edge | 55ms | 96.4% | 85 | Background after serving stale |
| SWR + Surrogate Key Purge | 51ms | 97.1% | 41 | Building Your Edge Prerender Pipeline in Practice