FSD Frontend System Design

Part III β€” The Speed Β· Lesson 6.7

HTTP Caching

In one lineTwo headers decide whether a request happens at all, and whether it costs a full download.

Where you've seen itA repeat visit where the JavaScript loads in 2ms from disk, and DevTools shows no network request whatsoever.

  • Time17 min
  • DiagramCache decision flowchart + fresh vs revalidate
  • Chapter6 Β· Database & Caching

Diagram

diagram
THE DECISION FLOW β€” the browser runs this for every request

  need a resource
        β”‚
        β–Ό
  in cache?  ── no ──────────────────────────► fetch from network
        β”‚ yes
        β–Ό
  still fresh?  (age < max-age)
        β”‚
   β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
  yes         no
   β”‚           β”‚
   β–Ό           β–Ό
 use it     revalidate: send If-None-Match: "<etag>"
 0ms,               β”‚
 no request   β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
              β–Ό            β–Ό
        304 Not Modified  200 + new body
        (headers only,    (full download)
         ~200 bytes)
diagram
THE TWO STRATEGIES β€” and when each is correct

  IMMUTABLE ASSETS (hashed filenames)
    Cache-Control: public, max-age=31536000, immutable
    app.7b2e04.js ──► never revalidated for a year
    new deploy ──► app.9d1f77.js ──► different URL, fresh fetch
    βœ“ zero requests, zero staleness. Requires content hashing.

  MUTABLE DOCUMENTS (HTML, API responses)
    Cache-Control: no-cache
    ↑ misleading name: "cache it, but ALWAYS revalidate first"
    ──► 304 in ~1 RTT if unchanged, cheap and always correct

  NEVER STORE (personal data on shared machines)
    Cache-Control: no-store, private
diagram
STALE-WHILE-REVALIDATE β€” instant AND eventually fresh

  Cache-Control: max-age=60, stale-while-revalidate=3600

  t=0     fetch, cache
  t=30s   βœ“ fresh          β†’ serve from cache
  t=300s  stale but within β†’ serve STALE instantly (0ms)
          the SWR window     + refresh in background
  t=2h    beyond window    β†’ blocking fetch

How it works

  1. Cache-Control sets freshness. While fresh, the browser doesn't ask the network at all β€” the fastest possible request is the one not made.
  2. ETag / Last-Modified enable revalidation. Once stale, the browser asks "has it changed?"; a 304 costs headers only.
  3. immutable + content-hashed filenames is the whole game for static assets: cache for a year and change the URL on deploy.
  4. Vary matters. Vary: Accept-Encoding is normal; Vary: Cookie effectively disables shared caching.
  5. Private vs shared: private means browser-only, public allows CDNs and proxies. Get this wrong on a personalised page and users see each other's data.

Trade-offs

Header comboResult
max-age=31536000, immutablePerfect for hashed assets
no-cacheAlways revalidate; cheap and safe for HTML
no-storeNever cached β€” auth pages, personal data
max-age=3600 on HTMLUsers stuck on an old app for an hour
s-maxageDifferent TTL for CDN vs browser

Interview angle

"You deployed but users still see the old version. Why?"

  • The HTML was cached with a max-age, so browsers never asked for the new one and kept referencing old asset URLs.
  • Correct split: no-cache on the HTML document, immutable + long max-age on hashed assets.
  • Purge the CDN for the document too β€” the edge can hold a stale copy even after the origin updates.

Recap

  • Fresh = no request at all; stale = a cheap 304 revalidation.
  • Hash your filenames and cache them for a year; never cache the HTML that points at them.
  • stale-while-revalidate buys instant responses without long-term staleness.