HTTP cache field guide · Tech

Three moments that decide which page appears

Follow the age of the stored response, then the request that triggers validation.

  1. A changed origin does not expire a fresh cache.With max-age=60, a stored page may remain the answer at second 50 even if the origin changed at second 40.
  2. The first stale-window visitor may see the old page.With stale-while-revalidate=300, a request at second 70 may receive the stored page while validation happens in the background.
  3. A quiet window does not refresh itself.If no request triggered validation, a request at second 400 waits for normal validation or retrieval.

A page has changed at the origin. A visitor reloads the same URL and still sees the old wording. That observation does not, by itself, prove that a purge failed or that the server returned a wrong 304. A cache may be following the freshness and stale-use policy in the response it already stored.

Consider one illustrative response with Cache-Control: max-age=60, stale-while-revalidate=300. We assume a single cache, a stable cache key, no competing directives, a successful origin update at second 40 and a validator that can obtain the changed representation. These are a reasoning model, not measurements of a particular browser or CDN. RFC 9111 defines age and freshness; RFC 5861 describes permission to serve stale while revalidating.

The origin edit does not end the fresh period

At second 0, the cache stores version A. The origin changes to version B at second 40. At second 50, version A is only 50 seconds old in this simplified example. Since the response's max-age is 60 seconds, the cache can use A without asking the origin. The edit changed the origin; it did not change the age of the stored response or announce itself to every cache.

The useful distinction is between origin state and cached representation. A fresh cached answer may be older than the current origin answer. That is the bargain of a 60-second freshness lifetime. If the content cannot tolerate even this interval, shortening only the stale window will not solve the first 60 seconds.

The first request in the stale window may still receive A

After the fresh period, the extra 300 seconds are a stale-use window, taking this simplified example to second 360. Suppose a request arrives at second 70. RFC 5861 permits a cache to return version A immediately while it attempts validation in the background. The visitor who triggered the check may therefore see A even when the origin already has B. Once the background check succeeds and replaces the stored response, a later visitor can receive B.

The word may matters. The extension permits this behaviour; it is not a promise that every cache implements it, that a check finishes before the next request, or that two cache layers refresh together. Nor is it a polling timer. Google's explanation of the browser feature makes the traffic dependency explicit: a request during the window starts the opportunity for a background update.

HTTP cache timeline: old page fresh through 60 seconds, possibly served stale while validating through 360 seconds, then normal validation at 400 seconds
The two later observations are separate traffic branches. A request at second 70 can refresh the entry; a request at second 400 after silence cannot assume that refresh happened.

The graphic separates three questions that are often collapsed into one: whether A is fresh, whether stale A may be used while validation runs, and whether any request arrived in time to trigger that work. Its times are relative to the stored response, not to a deployment clock.

A quiet cache makes the next visitor wait

Now take a separate branch of the example. No request arrives between seconds 60 and 360, so no traffic-triggered background revalidation occurs. At second 400 the stored A is outside the stale-while-revalidate window. Absent another rule that permits stale use, the cache cannot take the SWR shortcut; the request has to wait for normal validation or retrieval. That visitor may receive B after the check, but the response is no longer made fast by returning old A immediately.

This branch explains why the same policy can feel different on a popular page and an infrequently visited one. RFC 5861 explicitly points to traffic in the stale window as a condition for avoiding a blocking request. A long stale window increases the opportunity for a fast answer and also the interval during which an older answer may be served. It does not guarantee a cache refresh at the end of the interval.

Read the response age and the delivery path

The Age response field estimates how long a response has been resident across caches and transit since generation or successful validation. Under RFC 9111, a cache serving a stored response without validation sends an Age field. It is a more relevant clue to this policy than counting seconds since an editor pressed Publish. Record the response body marker as well: a header alone cannot identify which article revision the visitor saw.

A real site can have a browser cache, a shared CDN cache and more than one response variant. s-maxage can change the freshness lifetime of a shared cache; Vary affects which stored response matches a request. no-cache, must-revalidate, personalized responses and cache-specific rules can change the outcome. The simple 60-plus-300 timeline applies only to the stated policy and assumptions. Check the actual headers on the delivered path rather than treating the origin's configuration screen as proof of what the visitor received.

Run a release check with two distinct branches

First, record a baseline through the path readers use: response body marker, Cache-Control, Age, Date, ETag or Last-Modified where present, and any documented cache-status field. Keep the URL, cookies and request headers consistent. Publish a harmless test change at the origin and confirm B is really available there.

Then use a controlled cache entry to observe a request while A is fresh and another just after freshness expires. If the latter gets A, capture the upstream validation and a later request to see whether B replaces it. To test the no-traffic branch, use a separate fresh entry or reset the test environment and make no request during the SWR window; the second-400 observation is not a continuation of a second-70 request that already refreshed the entry. Document elapsed time from storage or successful validation, not merely from deployment.

The test can establish what this delivery path did under these conditions. It cannot establish a universal browser rule or the experience of every visitor. If an old validator incorrectly returns 304 for changed HTML, that is a different fault; our controlled ETag experiment shows how that failure can hide a correction after a check. The autosave lost-update test also uses HTTP conditionals, but protects a write rather than deciding when a cached read may be reused. The AMP origin handoff analysis addresses which server owns a visit, another reason to test the path actually serving readers.

Set the policy by the cost of an old answer

For a versioned static asset, a long freshness lifetime can be sensible because the URL changes with the content. For an unversioned page with a time-sensitive correction, explicitly decide how old an answer may be during the fresh period and the extra stale window. A theoretical maximum of 360 seconds in this example is a tolerance choice, not a predicted delay for every user. A successful background check can shorten it; cache eviction, request timing and implementation choices alter the observed sequence.

The practical release question is precise: how long may version A remain an acceptable answer after B exists, and what evidence will show which version a returning reader received? Choose headers and purge procedures from that requirement, then test both the busy and quiet branches. Do not infer a failed deployment from one old page, and do not infer a successful correction from one fresh origin response.

Primary HTTP sources