must-revalidate

Caching

must-revalidate is a Cache-Control response directive (RFC 9111): once a stored response goes stale, any cache — browser or CDN — must revalidate it with the origin before reusing it, and must return an error (normally 504) rather than fall back to the stale copy if the origin is unreachable.

9 min read Updated Aug 30, 2026

Full Explanation

must-revalidate is a response directive of the Cache-Control header. It tells a cache what to do once its stored copy of a response has gone stale. The stale copy must not be reused until the origin confirms it is still good. If the origin cannot be reached, the cache must generate an error instead of serving the stale copy. It binds both private (browser) caches and shared caches, such as a CDN edge. proxy-revalidate is the same rule, but it exempts private caches.

It is not a way to prevent caching, and it does not force a check on every request. That check is what no-cache does. While the stored response is still fresh, it is served from cache with no origin contact at all. must-revalidate only tightens what a cache may do after freshness runs out: it blocks stale reuse, and it blocks stale fallback during an outage. That is the trade: you get guaranteed freshness. In exchange, the edge can no longer ride out an origin failure.

How it works

Set must-revalidate alongside an explicit freshness lifetime. This is normally the max-age directive in seconds. If the response carries no explicit expiration time, a cache may assign a heuristic one of its own (RFC 9111, section 4.2.2). Then the moment revalidation starts becomes the cache's choice, not yours.

A typical response header looks like this:

Cache-Control: public, max-age=300, must-revalidate
  1. While the stored response is fresh, it satisfies requests directly. RFC 9111 says: “When a response is fresh, it can be used to satisfy subsequent requests without contacting the origin server, thereby improving efficiency” (RFC 9111, section 4.2).
  2. Once it becomes stale, a cache MUST NOT reuse it to satisfy another request until it has been successfully validated by the origin (RFC 9111, section 5.2.2.2).
  3. To validate, the cache sends a conditional request. This request carries the validators of its stored copy: the ETag in an If-None-Match field, and the Last-Modified timestamp in an If-Modified-Since field. A cache MUST send the entity tags if the stored response has them, and it SHOULD send Last-Modified as well. In practice, both are usually sent (RFC 9111, section 4.3.1).
  4. A 304 Not Modified means the stored response can be updated and reused, so the cache freshens it and serves it. A full response instead tells the cache that none of the responses it nominated are suitable. The cache MUST use the full response to satisfy the request (RFC 9111, section 4.3.3).
  5. If the cache is disconnected, it MUST generate an error response instead of reusing the stale response. The status SHOULD be 504 Gateway Timeout, unless another error status fits better (RFC 9111, section 5.2.2.2). 504 is the recommended default, not the only permitted answer.

The prohibition is general. It is not only about disconnection. A cache MUST NOT generate a stale response when an explicit in-protocol directive forbids it, and must-revalidate is named as one of those directives (RFC 9111, section 4.2.4). Normally a cache has an escape hatch when validation returns a 5xx: it can act as though the server failed to respond, and serve the stored response instead. That escape hatch is closed too, because that path is itself subject to section 4.2.4 (RFC 9111, section 4.3.3).

Why it matters for a CDN

A CDN edge is a shared cache. RFC 9111 defines a shared cache as “a cache that stores responses for reuse by more than one user” (RFC 9111, section 1). One stale representation on one edge is therefore served to everyone routed to that edge, until something replaces it. must-revalidate is the in-protocol way to stop that happening. It does not depend on a purge having already reached every node.

The specification is explicit about where it belongs. It is deliberately non-normative about it: the directive “ought to be used by servers if and only if failure to validate a request could cause incorrect operation, such as a silently unexecuted financial transaction” (RFC 9111, section 5.2.2.2). Apply that test to account balances, inventory counts, order state, and other state-carrying endpoints. The question is not “is this content important” but “does a stale answer break something”.

It also unlocks a category of authenticated traffic for shared caches. A shared cache MUST NOT use a cached response to a request that carried an Authorization header to satisfy any subsequent request, unless the response carries a directive that allows a shared cache to store it. RFC 9111 names exactly three that do: must-revalidate, public and s-maxage (RFC 9111, section 3.5). The cache must also conform to that directive. So with must-revalidate, the reuse stays subject to revalidation once stale.

The cost is the mirror image of the benefit. The edge stops absorbing origin failures for that content. Requests that would have been served a dated copy become errors instead.

What CDNs do

Support genuinely varies. One major CDN ignores the directive outright. Check your provider before relying on it.

  • Cloudflare honours must-revalidate through its Origin Cache Control feature. That feature is “enabled by default” for Free, Pro and Business customers, who “cannot disable it”. Enterprise customers choose per website. With it enabled, Cloudflare’s directive table reads “Does not serve stale. Must revalidate for CDN and for browser”. With it disabled, the same row reads “Cache directive is ignored and stale is served” (Cloudflare: Origin Cache Control).
  • Cloudflare and stale serving. With Origin Cache Control enabled, must-revalidate is listed among the directives that prevent Cloudflare serving stale content. So pairing it with stale-while-revalidate yields an EXPIRED cache status instead of UPDATING. The client waits for the origin (Cloudflare: Revalidation). must-revalidate likewise appears among the in-protocol directives that make Cloudflare ignore stale-if-error. Cloudflare also mirrors the RFC on authenticated traffic. With the feature on, content with an Authorization header “is cached only if must-revalidate, public, or s-maxage is also present”.
  • Cloudflare, setting the directive at the edge. A Cache Response Rule can add or remove must-revalidate on an origin response through the set_cache_control action. There it is one of the boolean directives taking set or remove. A cloudflare_only flag decides whether visitors also see the change (Cloudflare: Cache Response Rules settings).
  • Fastly does not honour it. Only public, private, max-age and s-maxage influence Fastly’s caching. “Directives ignored by Fastly include, but are not limited to, Cache-Control: no-cache, Cache-Control: no-store, and Cache-Control: must-revalidate”, and ignored directives are still passed through to the browser (Fastly: About cache control headers). Fastly’s own list of divergences from RFC 9111 states that must-revalidate and proxy-revalidate “are ignored in Compute”. It also states that a stale response “can still be reused during a stale-serving path such as stale-while-revalidate”. Fastly further notes that an Authorization header on the request does not by itself prevent caching, so the section 3.5 gate above is not the control it is on a conforming cache (Fastly: HTTP caching semantics).
  • Fastly, what to use instead. Note that Surrogate-Control is not a substitute for the directive: Fastly parses only max-age, stale-while-revalidate and stale-if-error from it. Edge revalidation is driven by the TTL instead. Set the edge lifetime with Surrogate-Control: max-age or s-maxage, and leave stale-while-revalidate and stale-if-error unset, since both default to 0 (Fastly: HTTP caching semantics).

Watch out for

  • An origin outage turns into errors. A conforming cache will not fall back to stale. So choose the directive only where a stale answer is genuinely worse than no answer.
  • It overrides the stale-serving extensions. RFC 9111 forbids generating a stale response when an in-protocol directive prohibits it. So combining must-revalidate with stale-if-error or stale-while-revalidate does not give you both behaviours. The stale extension loses (RFC 9111, section 4.2.4).
  • It constrains browser caches too. Unlike proxy-revalidate, must-revalidate reaches private caches. If you only want the shared cache to revalidate, and you are content for browsers to keep serving, proxy-revalidate is the narrower instrument (RFC 9111, section 5.2.2.8).
  • Revalidation is not free. Every stale hit becomes a conditional request to the origin. So many keys expiring together can produce a cache stampede at the origin. Request collapsing helps: a cache may combine multiple incoming requests into a single forward request, “thereby reducing load on the origin server and network”. But it does not remove the round trip (RFC 9111, section 4). Without validators on the response, each revalidation returns a full body rather than a cheap 304.
  • You may already have the shared-cache half of it. s-maxage incorporates the semantics of proxy-revalidate for a shared cache. So an s-maxage response already forbids stale reuse at the edge (RFC 9111, section 5.2.2.10).
  • It only binds caches that implement it. The directive is a requirement on conforming HTTP caches. A cache that ignores it, as Fastly does, will serve stale regardless. Verify the behaviour on your own edge rather than assuming the header is enough.

Best practice

  • Pair it with an explicit max-age so you, not the cache’s heuristic, decide when staleness begins. On its own the directive has nothing to act on until the response is already stale.
  • Always publish a validator: an ETag, a Last-Modified date, or both. This way, revalidation costs a 304 rather than a full re-download.
  • Reserve it for content where stale data causes incorrect operation: balances, inventory counts, order and account state. Apply the RFC’s if-and-only-if test rather than a general sense of importance.
  • Where a dated page beats an error page, choose the stale extensions instead: stale-while-revalidate serves stale immediately while revalidating in the background, and stale-if-error returns stale on an origin or network error rather than a hard error (RFC 5861, section 3 and section 4). Do not send both a stale extension and must-revalidate and expect the extension to win.
  • If only shared caches need to revalidate, send proxy-revalidate; if you are already sending s-maxage, that constraint is implied for shared caches.
  • Confirm your provider’s behaviour before depending on it. On Cloudflare Enterprise, check that Origin Cache Control is enabled. It is on and non-optional for Free, Pro and Business. On Fastly, expect the edge to ignore the directive and drive edge freshness with the TTL instead.

Examples

For financial data, combine a short TTL with must-revalidate:

Cache-Control: public, max-age=60, must-revalidate

This caches the response for 60 seconds. After that, the cache checks with the origin first. If the origin is down, the user gets a 504 instead of old figures.

In nginx, set this for your API endpoints:

location /api/account/balance {
    add_header Cache-Control "private, max-age=10, must-revalidate";
}

Frequently Asked Questions

must-revalidate is a Cache-Control response directive (RFC 9111): once a stored response goes stale, any cache — browser or CDN — must revalidate it with the origin before reusing it, and must return an error (normally 504) rather than fall back to the stale copy if the origin is unreachable.

For financial data, combine a short TTL with must-revalidate:

Cache-Control: public, max-age=60, must-revalidate

This caches the response for 60 seconds. After that, the cache checks with the origin first. If the origin is down, the user gets a 504 instead of old figures.

In nginx, set this for your API endpoints:

location /api/account/balance {
    add_header Cache-Control "private, max-age=10, must-revalidate";
}

Related CDN concepts include:

  • proxy-revalidate — proxy-revalidate is a Cache-Control response directive: once a shared cache's stored copy is stale, that …