How would you cache static assets so repeat visits are fast, without ever serving a stale file after a deploy?
The hashed-filename + immutable pattern that lets you cache aggressively AND deploy safely.
The tension is: you want long cache lifetimes (so repeat visits skip the network), but you also must guarantee users get the new file after a deploy. The standard solution decouples the two with content hashing. Give each built asset a filename derived from its content — app.9f3a2c.js. Because the name changes only when the content changes, you can serve it with Cache-Control: max-age=31536000, immutable — cache for a year, and 'immutable' tells the browser not to even revalidate on reload. On the next deploy the content changes → the hash changes → the filename changes → it's a cache MISS → the browser fetches the new file. The old cached file is simply never referenced again. The one file you must NOT cache long is the HTML that references these assets, because it carries the current hashed names; serve HTML with a short or no-cache policy (e.g., no-cache, which means 'revalidate every time' via ETag) so a deploy is picked up promptly. Under the hood the mechanics are: max-age sets freshness lifetime; ETag/Last-Modified enable cheap 304 Not Modified revalidation once stale; 'immutable' skips revalidation entirely. So the recipe is: hashed asset names + max-age=1yr, immutable for JS/CSS/images/fonts, and no-cache (or very short max-age) for the HTML entry point.
Configuring a CDN/static host; explaining why users still see old assets after a deploy.
# Hashed asset: safe to cache forever (name changes when content changes)
GET /assets/app.9f3a2c.js
Cache-Control: public, max-age=31536000, immutable
# HTML entry point: must reflect latest hashed names -> revalidate every time
GET /index.html
Cache-Control: no-cache # store, but revalidate with ETag before use
ETag: "v42"
# Repeat request when still fresh -> served from cache, no network
# After max-age expires -> conditional GET, server may reply 304