How does HTTP caching work with Cache-Control, ETag, and conditional requests?
Take a moment.
Say your answer out loud or write it down first — retrieval is what makes it stick. Reveal when you're ready.
Short answer
Cache-Control tells browsers and CDNs how long a response is fresh (max-age) and who may store it (public/private, immutable). Validators (ETag/Last-Modified) let caches revalidate stale copies with conditional requests (If-None-Match) — a match returns 304 with zero body. Strategy: immutable hashed assets cache for a year; HTML stays short-lived or uncached so deploys take effect.
Detailed explanation
Every cache decision answers two questions: may I store this, and for how long is it fresh? Cache-Control encodes both: max-age=31536000 allows a year of reuse with zero requests; no-store forbids all storage (personal data, checkout responses); private allows only the browser, not shared CDNs (per-user content); public allows CDNs (marketing assets); immutable tells the browser a fingerprinted URL will never change content, so even reload skips revalidation. When freshness expires, the cache revalidates instead of re-downloading: it sends If-None-Match with the stored ETag (a content hash) or If-Modified-Since with the stored date. If the origin's current ETag matches, it answers 304 Not Modified — headers only, no body — and the cache extends freshness. Mismatch returns the new 200 with a new ETag. The production pattern this enables: filenames with content hashes (app.a3f9c1.js) served immutable for a year, while index.html ships no-cache (always revalidate) so each deploy points at new hashes — users get instant updates plus near-100% cache hits. Invalidation falls out naturally: changed content gets a new filename, so there is nothing to purge. Watch for: caching authenticated responses publicly (leaking one user's data to everyone — use private or Vary: Cookie), and heuristic caching of responses with no explicit headers, which intermediaries may store unexpectedly.
Example
# Fingerprinted asset: cache aggressively, purge never needed
# GET /static/app.a3f9c1.js
# → 200 OK
# Cache-Control: public, max-age=31536000, immutable
# ETag: "a3f9c1"
# Entry HTML: always revalidate so deploys land immediately
# GET /index.html
# → 200 OK
# Cache-Control: no-cache
# ETag: "v42"
# Next visit → If-None-Match: "v42" → 304 Not Modified (0 bytes)Interview tip
State the two-question model (may I store, how long fresh), then give the fingerprint-plus-no-cache-HTML pattern with its headers. That deploy-safe strategy is the production answer.
Common mistakes
- Caching HTML for a year so users never receive deploys.
- Serving per-user content as public, leaking data across users.
- Changing file contents without changing the fingerprinted filename.