Stale-While-Revalidate
Serve cached content immediately while refreshing it in the background, eliminating cache-expiry latency spikes.
Metrics
This technique can move up to 67% of LCP's share of the Lighthouse score.
This technique does not move TBT.
This technique does not move CLS.
This technique can move up to 67% of FCP's share of the Lighthouse score.
This technique can move up to 33% of SI's share of the Lighthouse score.
This technique can move up to 100% of TTFB's share of the Lighthouse score.
Overview
stale-while-revalidate is a cache directive that allows a server or CDN to serve a stale (expired) cached response immediately to the visitor while simultaneously triggering a background refresh of the cache. The visitor never waits for regeneration — they receive the previous cached version in milliseconds, and the next visitor receives the freshly generated version. It eliminates the ‘thundering herd’ problem where cache expiry causes a burst of simultaneous uncached requests all hitting the origin at once.
Impact grade, A+ to D: how much of the Lighthouse score this technique can move. Unaffected: TBT, CLS.
The Problem
Standard cache TTL behavior creates a predictable latency spike: at the moment of expiry, the cached page is gone. The first visitor at that moment triggers a full PHP + database page generation — which may take 500ms–2s — and waits for it. Every concurrent visitor during that generation window also hits the uncached origin. On high-traffic pages with a short cache TTL, this creates periodic performance degradation for every visitor who happens to land during a cache refresh cycle.
Performance Impact
LCP
Consistent
LCP remains cache-fast even at the moment of TTL expiry — no latency spike
FCP
Eliminates spikes
no visitor ever waits for cache regeneration — stale content is served instantly
SI
Consistent
consistent cache delivery means Speed Index never degrades at cache expiry boundary
TTFB
High
TTFB never degrades to uncached PHP execution time — always serves from cache
Risk: Low
visitors may see content slightly older than the TTL — acceptable for most content types
How it works
01
The HTTP Cache-Control directive
Cache-Control: max-age=3600, stale-while-revalidate=86400tells CDNs and reverse proxies: serve this page from cache for 1 hour fresh. After that, the cache is ‘stale’ — but continue serving it for up to 24 additional hours while a background revalidation request refreshes it. The visitor sees no latency; the refresh happens invisibly.02
CDN-level implementation
Major CDNs — Cloudflare, Fastly, Varnish — honor
stale-while-revalidatenatively. When the cached response expires, the CDN edge node serves the stale copy to the requesting visitor and asynchronously makes a request to the origin to refresh the cached version. No origin hit occurs from the visitor’s perspective.03
Server-side implementation with WordPress
Add the directive to pages via the
send_headershook or by configuring Nginx headers. For page cache plugins, WP Rocket supports stale cache serving natively. LiteSpeed Server supports it via theX-LiteSpeed-Cache-Controlheader with astale-while-revalidatevalue.04
Stale cache as a fallback for origin errors
The related directive
stale-if-errorextends stale serving to cover origin errors:Cache-Control: max-age=3600, stale-if-error=86400. If the origin returns a 5xx error during revalidation, the CDN continues serving the stale cached version rather than propagating the error to visitors — a form of automatic high-availability.
Code Example
# Standard cache: at TTL expiry, visitors wait for full PHP regeneration
Cache-Control: max-age=3600
# Timeline:
# t=0 → cache populated
# t=3600 → cache EXPIRED, first visitor waits 800ms for regeneration
# t=3601 → new cache populated, subsequent visitors fast again# Stale-While-Revalidate: stale content served instantly, refresh in background
Cache-Control: max-age=3600, stale-while-revalidate=86400, stale-if-error=86400
# Timeline:
# t=0 → cache populated
# t=3600 → cache 'stale' — but visitor still served instantly from stale copy
# → background revalidation request fires to origin
# t=3602 → new cache populated, next visitor gets fresh versionIn WordPress with Nginx, add to your server block: add_header Cache-Control "public, max-age=3600, stale-while-revalidate=86400";. For WP Rocket: the ‘Keep current cache while updating’ option implements this pattern.
Limitations
- The stale-while-revalidate window means visitors may see content up to (max-age + stale-while-revalidate) seconds old in the worst case. Not suitable for pages with real-time data requirements — stock prices, live scores, breaking news.
- Browser caches also honor stale-while-revalidate for assets, but the browser revalidation happens on the user’s next navigation, not immediately — the user may see stale CSS or JS until they reload.
- Not all CDNs honor the stale-while-revalidate directive from the
Cache-Controlheader — some require proprietary configuration. Verify CDN support before relying on it.
Common Mistakes
Setting stale-while-revalidate on pages with personalized or session-specific content
Stale-while-revalidate is designed for public, cacheable responses. Applying it to logged-in pages or pages with user-specific content risks serving one user’s session data to another. Always pair with private or no-store for authenticated responses.
Using stale-while-revalidate as a substitute for cache warming
SWR eliminates the penalty for the first visitor after expiry, but that first visitor still triggers origin load. For very high-traffic pages, cache warming (proactively regenerating before TTL) is more reliable. Use both: SWR as a safety net, cache warming as the primary strategy.