How it works
The explanation, with examples you can run.
Freshness
A stored response is fresh until its age passes max-age seconds. A fresh response is used straight from the cache, with no network request at all. Once it is stale, the cache must revalidate before using it. Useful directives:
max-age=N: fresh for N seconds.s-maxage=Ndoes the same for shared caches only, overridingmax-agethere.public/private:privatemeans only the user's browser may store it (personalised pages); shared caches must not.no-cache: may be stored, but must be revalidated before every use.no-store: must not be stored anywhere.immutable: the response won't change while it is fresh, so don't revalidate even when the user reloads.stale-while-revalidate=N: after it goes stale, it may still be served for N seconds while a fresh copy is fetched in the background.
Without Cache-Control or Expires, caches may guess a freshness lifetime from Last-Modified (heuristic caching). Set the headers yourself rather than relying on that.
Revalidation
When the server sends an ETag (a version tag for the body) or Last-Modified, a cache holding a stale copy sends a conditional request with If-None-Match or If-Modified-Since. If the content hasn't changed, the server answers 304 Not Modified with headers only, and the cache updates its stored copy with them, which makes it fresh again. You still pay a round trip, but not the download.
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
This is a simplification of RFC 9111 (it ignores Expires, Vary and heuristics), but it is the decision every cache makes.
A pattern that works
- Hashed static assets (
app.3f9a1c.js,logo.81b2.svg):public, max-age=31536000, immutable. Your build tool writes the hash; a deploy changes the URL. - HTML:
no-cachewith anETag, or a shortmax-ageat the CDN, so a new deploy is picked up on the next visit. - Personalised responses:
private, andno-storefor anything sensitive. - Responses that differ by request header (language, encoding): send
Varyso a cache doesn't hand one user's variant to another.
A CDN is a shared cache close to the user. It follows the same headers, can be purged when you must, and usually adds its own controls on top. Purging is the escape hatch; hashed URLs are the plan.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.