Lesson 5 of 16
26. CDNs

26.4 How do cache keys and headers work?

3 min read+10 XP

In the warehouse: Every carton has a label saying exactly what it is, so a warehouse doesn't stock "Marie biscuits, 200 g" fifty times under fifty slightly different names. And every carton has a use-by date, so the warehouse knows when to stop selling it.

In the system: Two things decide what a CDN caches: the cache key (which requests count as the same object) and the headers (whether, and for how long, to keep it).

tracking parameters create 50 keys for one cover; Cache-Control headers for assets, pages and private data.

Figure: get the key right so copies are shared; get the headers right so the right things are cached for the right time.

The cache key is usually the host + path + query string, plus any request headers listed in Vary:

  • Strip noise. ?utm_source=ig and ?utm_source=wa are the same cover. In the chapter's code, 50 links with different tracking parameters create 50 cache entries (50 misses); normalised, they're 1.
  • Sort query parameters, so ?q=dune&page=2 and ?page=2&q=dune match.
  • Be careful with Vary. Vary: Accept-Encoding is fine (a few variants). Vary: Cookie or User-Agent creates almost one entry per user and destroys your hit ratio.

The headers come from the origin's Cache-Control:

DirectiveMeaning
publicAny cache (browser, CDN) may store it
privateOnly the user's browser may store it, never a shared cache
no-storeNobody stores it
max-age=NFresh for N seconds (browsers and CDNs)
s-maxage=NFresh for N seconds in shared caches only (overrides max-age for the CDN)
immutableWill never change at this URL; don't even revalidate
stale-while-revalidate=NServe the old copy for up to N seconds while fetching a new one in the background
stale-if-error=NServe the old copy for up to N seconds if the origin is failing

Plus validators: ETag and Last-Modified let a cache ask "has it changed?" (If-None-Match) and get a tiny 304 Not Modified instead of the whole file.

Typical choices for the bookstore:

  • JS, CSS, covers with versioned file names (app.3f9a1c.js, cover-42-v3.jpg): public, max-age=31536000, immutable. A change gets a new URL, so nothing ever needs purging.
  • Book pages (same for everyone, change occasionally): public, max-age=60, s-maxage=300, stale-if-error=600. Browsers check often, the CDN keeps it 5 minutes, and it survives an origin outage for 10.
  • Account, cart, checkout: private, no-store. Never in a shared cache (the Level 4 bug).

Memory code: "One label per product, and a use-by date on every carton."