Stale-While-Revalidate Planner
Plan max-age and stale-while-revalidate values and output the header.
stale-while-revalidate (SWR) is a caching strategy that, right after a cached response expires, still serves the stale copy to the user instantly while quietly fetching a fresh version in the background. This removes the slow requests that pile up at the expiry boundary and lets you respond at cache speed almost every time while keeping content reasonably fresh.
This planner takes max-age, stale-while-revalidate, an optional stale-if-error, and a public/private scope, then assembles a ready-to-use Cache-Control header. It also converts each window into seconds and minutes and explains, on a timeline, how the response behaves depending on when a request arrives.
Cache-Control: public, max-age=60, stale-while-revalidate=600
| Fresh window (0 to max-age) | 60s (1 min) |
|---|---|
| Background refresh window (swr) | 600s (10 min) |
| Total stale allowance (max-age + swr) | 660s (11 min) |
| Served on error (stale-if-error) | — |
- 0 to 60s: fresh. The cache responds instantly with no origin call.
- 60 to 660s: serve stale immediately + revalidate origin in the background. No user wait.
- after 660s: SWR window closed. The next request does a synchronous revalidation (waits for origin).
What each directive means
In the Cache-Control response header, these directives tell caches (browsers and CDNs) how to treat the response.
- max-age=N: how long (in seconds) the response is considered fresh. During this window the cache answers directly without asking the origin.
- stale-while-revalidate=N: for N seconds after
max-ageexpires, the cache may return the stale response immediately and, at the same moment, revalidate (re-fetch from origin) in the background. The user never waits. - stale-if-error=N: if the origin errors (5xx) or is unreachable, the cache may serve the stale response for up to N seconds past expiry, masking the outage. It is an availability insurance.
- public / private:
publicallows shared caches like CDNs to store the response;privaterestricts storage to the end user's browser (per-user content).
The response timeline
Calling the moment a response is stored in the cache time 0, the cache behaves as follows depending on when the next request arrives.
- 0 to max-age: the fresh window. The cache responds instantly with no origin call.
- max-age to (max-age + swr): the background refresh window. The user gets the stale response immediately while the cache fetches a new copy from the origin. Perceived latency is zero.
- after (max-age + swr): the SWR window has closed, so a stale response is no longer allowed. The next request waits for a synchronous revalidation to complete.
So a short max-age with a long stale-while-revalidategives you "almost always fast responses plus steady background refresh." Pair it with the Cache-Control builder when assembling the full header, and design expiry times for temporary-access signed URLs with the signed URL expiry calculator.
Benefits and caveats
Key benefits of SWR:
- It removes the "thundering herd" of cache misses and the slow first request at the expiry edge.
- Nearly every response is served at cache speed, so TTFB stays stable.
- Adding
stale-if-errorlets you ride out an origin outage on the last good copy.
Things to watch:
- It does not guarantee users always see the newest copy. During the SWR window they may get data that is one step behind, so it is unsuitable for responses that need instant accuracy like prices or stock counts.
- Not every browser or CDN supports SWR identically. Unsupported caches usually treat the response as fresh only up to max-age.
- Background refreshes still produce origin requests. With a very long swr window, low-traffic resources may refresh rarely and serve increasingly stale responses.
Recommended presets by content type
Here are combinations engineers reach for, grouped by the nature of the content. Treat them as starting points and tune them against your traffic pattern and tolerable staleness. s-maxage is freshness that applies only to shared caches like CDNs; if you supply it, this planner emits it alongside max-age in the header.
| Content type | Suggested Cache-Control | Intent |
|---|---|---|
| News / home feed (changes often) | public, max-age=60, stale-while-revalidate=300 | Fresh for 1 min, then 5 min of background refresh |
| Blog post / product page | public, max-age=600, stale-while-revalidate=86400, stale-if-error=604800 | 10 min fresh, invisible refresh for a day, ride out errors for a week |
| API JSON (dashboard) | private, max-age=0, stale-while-revalidate=30 | Always cache-speed, auto-refresh within 30 s |
| Hashed static asset (app.abc123.js) | public, max-age=31536000, immutable | Immutable for a year — the filename changes when content does, so SWR is unneeded |
Walkthrough: tracing requests by arrival time
Say the header is Cache-Control: public, max-age=60, stale-while-revalidate=300 and the response was stored in the cache at 12:00:00. Freshness expires at 12:01:00 (+60 s), and the SWR window closes at 12:06:00 (+360 s).
- Request at 12:00:30: fresh window → the cache answers instantly, zero origin calls.
- Request at 12:03:00: SWR window (120 s past expiry) → the user gets the stale copy immediately while the cache re-fetches the origin in the background and swaps in the fresh copy. Whoever made this request sees data one step behind.
- Request at 12:03:01 (right after): if the background refresh just finished, this one gets the refreshed response, and the freshness timer restarts from 60 s based on that refresh time (around 12:03:00).
- Request at 12:10:00: past the SWR close (12:06:00), so a stale response is no longer allowed → the request waits for a synchronous origin revalidation before responding.
Common pitfall
The most frequent misconception is believing "something refreshes the cache for me during the SWR window." The background revalidation is only triggered by an incoming request. With no traffic the cache just keeps aging, and the next visitor who eventually arrives finds the SWR window already closed and inherits the slow synchronous revalidation. In other words, stale-while-revalidaterefreshes on the next request, not on a clock. One more trap: pairing it with no-cache orno-store nullifies SWR, so do not mix them.
Frequently asked questions
What happens with max-age=0, stale-while-revalidate=60?
Is stale-while-revalidate added to max-age or included in it?
Do I need stale-if-error?
Does this tool send my input to a server?
Can the CDN and the browser cache behave differently?
Related guides
- Cache-Control Done Right: Making Browser Caching WorkWhat max-age, no-cache, no-store and immutable really mean, with recommended policies per resource type.