Cheat Sheet · HTTP Caching

growing

Cache-Control

Which directive to ship — and where the one-time-value case forces your hand.

·

http, caching, csp, security, web, cheatsheet

The common path, not the whole law: Cache-Control directives compose more freely than any tree implies. Start here, then reach for the reference at the end. The companion essay, The Header That Can't Be Cached, is the why.

Which directive do I ship?

  • Does the body carry a per-response one-time value?

    a CSP nonce, a one-time CSRF token rendered into the HTML

    • yes no-store the nonce lands here — a stored body is a reused nonce
    • no

      Is the response identical for every user?

      • no private browser may store; add no-cache to revalidate each use — unless the body holds a one-time value, which is the yes branch above
      • yes

        Is the URL content-addressed / fingerprinted?

        • yes max-age=31536000, immutable hashed assets — cache a year, never revalidate
        • no max-age=…, must-revalidate or no-cache to force a check on every reuse

no-cache is not no-store

The trap at the center of the topic. The names are a historical accident.

  • no-cachestore it, but revalidate before reuse. A copy is kept; it just can't be served without checking first.
  • no-storekeep no copy at all. The only directive that forbids storage, anywhere, by any cache. This is the one a nonced page needs.

Who stores it

  • private cache — the browser. What it stores, only that reader sees.
  • shared cache — a CDN, proxy, gateway. What it stores, everyone behind it sees. private and s-maxage speak to these only.

How long it's fresh

  • max-age=N — fresh for N seconds from generation, for every cache.
  • s-maxage=N — overrides max-age for shared caches only; the usual split is s-maxage=3600, max-age=60 (CDN an hour, browser a minute). Once stale under s-maxage, a shared cache must revalidate — it implies proxy-revalidate.
  • stale-while-revalidate=N (RFC 5861) — may serve the stale copy for N seconds while refetching in the background; stale-if-error is its outage-tolerant sibling.
  • No header at all — not "no caching": caches may assign a lifetime heuristically (RFC 9111 §4.2.2 suggests ~10% of the time since Last-Modified). If you care, say so explicitly.

The two extremes

  • must-revalidate — once stale, may not be served again until rechecked.
  • immutable — won't change while fresh, so don't even revalidate on reload. What you put on app.9f3c.js. (Firefox and Safari honor it; Chrome never implemented the directive — it changed reload behavior instead.)

Why a nonce forces no-store

A nonce is unique per response; a cache's whole job is to reuse a stored response. private moves where the copy lives; no-cache governs when it's rechecked — a 304 still serves the stored (stale-nonce) body. Neither stops reuse. Only no-store does.

Keep your cache anyway

  • Static / content site — vouch by hash (sha256-…), not nonce. Stable across responses, so it caches forever. Ship public, max-age=…, immutable.
  • Need nonces + caching — inject the nonce at the edge (CDN worker). Origin stays cacheable; only the last hop is unique.

Don't bother

  • Pragma: no-cache on a response — never specified, so no cache is required to honor it (RFC 9111 §5.4). A deprecated HTTP/1.0 request header; ship Cache-Control.
  • public by reflex — responses are shared-cacheable by default once fresh. public overrides a prohibition; it doesn't grant permission you have.

Outside HTTP's jurisdiction

  • A service worker's Cache API ignores Cache-Control entirely — your code stores and expires entries itself.
  • The back/forward cache restores a snapshot of the live page; history navigation may show expired content regardless of directives.
  • Vary decides the cache key (which request headers make responses distinct); wrong Vary is its own class of bug this page doesn't cover.

The reference

RFC 9111 is the law; MDN's Cache-Control page is the readable version; the companion essay is the why.

Type to search · ↑↓ to move · ↵ to open · Esc to close