HTTP Caching
- Why it matters
- Cache-Control directives
- Revalidation
- ETags vs Last-Modified
- Heuristic caching
- Cache busting
[!note] Test note for the books library launch. Covers the stuff I keep looking up.
Why it matters
Every uncached request costs: latency + bandwidth + server load. Caching eliminates repeat work. The browser, CDN, and reverse proxy all participate.
Cache-Control directives
| Directive | Meaning |
|---|---|
max-age=N |
Resource fresh for N seconds from request time |
s-maxage=N |
Same, but for shared caches (CDNs). Overrides max-age |
no-cache |
Must revalidate before using cached copy (not “don’t cache”) |
no-store |
Never cache at all |
immutable |
Content will never change — skip revalidation forever |
private |
Only browser cache, not CDN |
stale-while-revalidate=N |
Serve stale while fetching fresh in background |
[!warning]
no-cachedoes NOT mean “don’t cache”. It means “always revalidate”. Useno-storeif you want nothing cached.
Revalidation
When a cached response is stale, the browser revalidates instead of refetching the whole thing:
GET /style.css HTTP/1.1
If-None-Match: "abc123"
If-Modified-Since: Mon, 12 May 2026 10:00:00 GMT
Server responds either:
- 304 Not Modified (no body, just headers) → browser uses cached copy
- 200 OK + new body → new content
ETags vs Last-Modified
- ETag: opaque hash of content. More reliable — works for sub-second changes.
- Last-Modified: timestamp. Coarser, but simpler to implement server-side.
Prefer ETags when you can.
Heuristic caching
If no Cache-Control header is set, browsers apply heuristic caching:
Usually caps at 24 hours. Don’t rely on this — always set explicit headers.
Cache busting
For versioned assets (/app.abc123.js), use:
Cache-Control: public, max-age=31536000, immutable
The hash in the filename guarantees a new URL on change. The browser never needs to revalidate.
[!tip]
immutabletells the browser to not even bother revalidating during themax-agewindow — no extra network round trip even on hard refresh.