Contact

What is Cache-Control?

Definition

Cache-Control is the HTTP header that tells caches such as browsers and CDNs whether a response may be stored, how long it stays fresh and what must happen once it goes stale. Its value is a comma-separated list of directives like max-age, s-maxage, no-cache, no-store, private, immutable and stale-while-revalidate. Requests can carry it too, but caching policy is defined mainly by the response header.

Also known as: Cache-Control header, cache headers, max-age, s-maxage, stale-while-revalidate, no-cache vs no-store

Timeline of a cached response served fresh until max-age expires, then becoming stale and revalidated with a 304

One header, several caches

On its way to the user a response may pass through two kinds of cache: the private cache inside the user's browser, and shared caches that serve many users, such as a CDN or a reverse proxy. Cache-Control instructs all of them at once. Some directives apply everywhere; others only matter to shared caches.

Directives are comma-separated and order does not matter. When they conflict, the most restrictive one wins, so no-store overrides everything else. One subtlety catches people out: max-age counts from when the origin generated the response, not from when the browser received it. A response that sat in a CDN for five minutes arrives with age: 300, and with max-age=600 it stays fresh in the browser for only another 300 seconds.

Response directives at a glance

DirectiveMeaning
max-age=NReuse for N seconds without contacting the server.
s-maxage=NLifetime for shared caches only; overrides max-age there and is ignored by browsers.
no-cacheMay be stored, but must be revalidated with the origin before every reuse.
no-storeNo cache may store the response at all.
privateOnly the browser may store it; CDNs may not.
publicShared caches may store it, which matters most for responses to requests carrying an Authorization header.
must-revalidateOnce stale, it cannot be reused without revalidation; if the origin is unreachable the cache returns 504 rather than the old copy.
immutableThe body will not change while fresh, so even a reload need not revalidate it.
stale-while-revalidate=NFor N seconds after expiry, serve the stale copy immediately and refresh it in the background.
stale-if-error=NIf the origin answers 500, 502, 503 or 504, the stale copy may be served for up to N seconds.

no-cache is not "don't cache"

The name misleads almost everyone at first. no-cache means "store it, but check before using it". The browser sends its stored ETag, and if nothing changed the server replies with a bodyless 304 Not Modified. That makes it a good default for HTML that can change at any time. When nothing should be kept anywhere, such as payment confirmations, personal documents or one-time tokens, the directive you want is no-store. If you have to accommodate very old HTTP/1.0 caches, max-age=0, must-revalidate achieves the same as no-cache. Note that neither directive guarantees revalidation on Back navigation, because the back/forward cache keeps the whole page in memory.

Different lifetimes for the CDN and the browser

Splitting freshness between shared caches and the browser is where Cache-Control earns its keep:

# Product listing API: 1 min in the browser, 5 min on the CDN, then
# serve the old copy for up to 1 min while refreshing in the background
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=60

# Signed-in user's account page
Cache-Control: private, no-cache

# Tolerate a day-old copy if the origin goes down
Cache-Control: max-age=600, stale-if-error=86400

# Response containing a one-time verification code
Cache-Control: no-store

"Serve stale, refresh behind the scenes" is also the idea behind ISR in Next.js. Some CDNs additionally honour headers addressed only to them; CDN-Cache-Control is the standardised example (RFC 9213). Defaults vary a great deal between providers, so check your CDN's own documentation for which directives it respects.

Browser support quirks

  • stale-while-revalidate works in the browser cache since Chrome 75, Firefox 68 and Safari 14.
  • immutable is supported by Firefox and Safari but not implemented in Chrome. Chrome does not revalidate fresh subresources on a normal reload anyway, so the practical gap is small.
  • The request directives max-stale and min-fresh are not supported by major browsers. A hard reload (Ctrl+Shift+R) adds Cache-Control: no-cache to its requests.

The Network panel in your browser shows the Cache-Control value of every response. For how stored responses are reused day to day, see the browser cache entry; MDN documents every directive. The SEO Checker also lists the Cache-Control header of the page response.

Related terms

← Back to the glossary