Learn
The Future of CDN Technology: Trends and Predictions
CDN Trends 2026: The Future of CDN Playbook The most useful CDN trend of 2026 isn't a feature. It's a repricing. Bulk ...
Six directives decide whether your CDN protects your origin or just proxies to it: max-age, s-maxage, stale-while-revalidate, stale-if-error, must-revalidate, and immutable. The one most teams ship without is stale-while-revalidate, and it is the one that removes the blocking origin fetch from the user path. On a hot object with a 60-second freshness lifetime spread across 40 edge caches, adding s-maxage=600, stale-while-revalidate=86400 cuts origin revalidations from roughly 2,400 to 240 per hour while every user request is answered from cache in single-digit milliseconds. This is a Cache-Control problem, not a vendor problem.

Shared caches and private caches read the same header and reach different conclusions. RFC 9111 defines the freshness model, RFC 5861 adds the two stale extensions, and RFC 8246 adds immutable. Everything else in the header is either a permission or a prohibition. The table below covers only directives that measurably change what an edge node does.
| Directive | Effect at the CDN edge | Most common mistake |
|---|---|---|
| max-age | Freshness lifetime for every cache, browser included | Using one value for both browser and edge, so a purge cannot reach the browser copy |
| s-maxage | Overrides max-age for shared caches only; also overrides Expires | Assuming it is additive to max-age instead of a replacement |
| stale-while-revalidate | Grace window in which a stale response is served immediately while the edge revalidates asynchronously | Expecting background refresh without traffic; the window only advances when a request arrives |
| stale-if-error | Permits serving stale on origin 5xx, connect failure or timeout, for the stated seconds past expiry | Setting it long enough to hide a multi-hour origin outage from dashboards |
| must-revalidate | Forbids any stale delivery; silently disables both stale extensions | Shipping it alongside stale-while-revalidate and wondering why nothing changed |
| proxy-revalidate | Same prohibition, shared caches only, browser keeps its grace | Confusing it with private |
| no-cache | Store allowed, reuse only after successful validation | Used where no-store was meant, leaving copies at rest in shared caches |
| private | Removes the object from the shared cache entirely; hit ratio goes to zero | Emitted by a framework default on pages that are fully public |
| immutable | Suppresses reload-triggered revalidation for fingerprinted assets | Applied to URLs whose bytes can change under the same path |
If you only change one thing after reading this table, separate browser TTL from edge TTL with max-age plus s-maxage, then add a stale window on top.
Without a stale window, expiry is a cliff. The first request after the freshness lifetime ends pays the full origin round trip, and it is a real user. In 2026 measurements, a cross-region origin fetch for dynamic HTML lands in the 150–400 ms range once connect, TLS reuse, origin compute and transfer are counted, against 3–10 ms for an edge hit. That single request is a rounding error in p50 and it owns your p99.9.
The multiplier is cache fan-out. Each independent cache node that holds the object revalidates on its own schedule unless a shield tier collapses the requests.
stale-while-revalidate, defined in RFC 5861, lets a shared cache return an expired response instantly and refresh it in the background. On an object held by 40 independent edge caches, moving from s-maxage=60 to s-maxage=600, stale-while-revalidate=86400 reduces origin revalidations from about 2,400 to 240 per hour, a 10x drop, and none of the remaining 240 are on a user's critical path. As of 2026 most CDNs honor the directive on shared caches, while browser support stays uneven: Chromium-based browsers apply it in the private cache, others largely ignore it.
Personalised-free HTML, short edge TTL, long grace:
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=86400, stale-if-error=259200
Fingerprinted JS or CSS bundle:
Cache-Control: public, max-age=31536000, immutable
API read model, JSON, tolerant of 5 seconds of drift:
Cache-Control: public, max-age=0, s-maxage=5, stale-while-revalidate=30, stale-if-error=600
HLS or DASH media playlist, 6-second segments:
Cache-Control: public, max-age=2, s-maxage=2, stale-while-revalidate=6
Media segment, immutable by URL:
Cache-Control: public, max-age=604800, s-maxage=2592000, immutable
Authenticated dashboard payload:
Cache-Control: private, no-store
Set max-age=0 with a non-zero s-maxage when you want purge authority: the browser always asks the edge, and the edge is the only place you have to invalidate.
If your origin sits behind nginx and you also cache there, the equivalent knobs are directive-level rather than header-level: enable background updates with proxy_cache_background_update on, allow stale delivery with proxy_cache_use_stale updating error timeout http_500 http_502 http_503 http_504, and collapse concurrent misses with proxy_cache_lock on. Varnish expresses the same idea as grace plus a background fetch.
Stale delivery is a correctness concession you are choosing on purpose. Four failure modes worth naming.
The observability gap is that most teams cannot tell a fresh hit from a stale hit. RFC 9211 defines the Cache-Status response header, whose fresh and ttl parameters make that distinction explicit; where a CDN emits a proprietary status token instead, map STALE and UPDATING states into the same metric. Configurable response headers and log fields per cache tier are the practical requirement here, which is why edge-level control belongs on the checklist when you review CDN edge caching and header control features rather than being treated as a detail.
It fits catalogue pages, marketing pages, search result pages, API read models, playlists, config blobs, and anything where a few seconds of drift is cheaper than a slow response. It does not fit inventory counts at checkout, payment state, authorization decisions, or one-time tokens. For those, private, no-store is the answer and the caching win has to come from moving the read model, not the response.
stale-while-revalidate defines a grace period after a response's freshness lifetime during which a CDN may serve the cached copy immediately and revalidate it against the origin in the background. The user gets an edge hit in single-digit milliseconds instead of a 150–400 ms origin fetch. The refresh only fires when a request arrives.
max-age sets the freshness lifetime for all caches; s-maxage overrides it for shared caches such as a CDN and also overrides the Expires header. Pairing max-age=0 with a non-zero s-maxage keeps browsers asking the edge on every navigation while the edge still absorbs the traffic, which makes purge the single source of truth.
No. stale-if-error applies to 500, 502, 503, 504, connection failures and timeouts. A 404 or 410 is a valid answer from a healthy origin, so it replaces the cached entry rather than triggering stale delivery. Deleting a record at origin will therefore propagate to users even with a long stale-if-error window.
Check for must-revalidate or proxy-revalidate in the same Cache-Control header, since both forbid stale reuse outright. Other causes: private or no-store on the response, an origin-side cache configuration overriding response headers, a Surrogate-Control header taking precedence, or a purge that evicted the stale copy instead of marking it stale.
Pick the ten highest-volume cacheable URLs on your service. Log Age, Cache-Status and origin requests per second for 24 hours as a baseline. Then change one thing: raise s-maxage by 10x and add stale-while-revalidate equal to ten times that value, leaving max-age untouched. Re-measure for another 24 hours and compare origin RPS, p99 TTFB, and the share of responses served stale. If origin load does not fall at least 3x, your problem is cache-key fragmentation, and that is the next thing to fix.
Learn
CDN Trends 2026: The Future of CDN Playbook The most useful CDN trend of 2026 isn't a feature. It's a repricing. Bulk ...
Learn
Classic HLS with 6-second segments and a three-segment player buffer sits 18–30 seconds behind the camera. CMAF chunked ...