Skip to main content

Caching

  • Caching is layered; each layer has different cost, granularity and invalidation.

In day-to-day conversation “cache it” can refer to:

  1. A CPU register that avoids a RAM fetch
  2. A Redis hash that avoids a SQL query
  3. A CDN edge that avoids a trans-Atlantic round-trip
  4. A browser memory object that avoids a 30 ms HTTP call
  5. A Service-Worker entry that lets the app start while the phone is in airplane mode

All of those are “caching”, yet each layer has different granularity, lifetime, invalidation mechanism and failure mode.

If you treat them as one concept you will over-cache, under-cache, or ship invisible bugs that only show up on the customer’s machine.

  • (where, freshness, invalidation, spectrum)

1. Decide where the bits may live (caching layers)

LayerTypical latency savedWho owns it?How to wipe it?
Browser memory (tab open)0-10 msJS codeF5, close tab
Browser HTTP cache10-300 msBrowser + HTTP headersCtrl-F5, Clear-Site-Data header
Service-Worker cache0-∞ (offline)Your JScaches.delete() or new SW install
CDN / edge50-500 msOps / Cloud consolePurge API
Reverse-proxy (Nginx, Varnish, Envoy)1-20 msDevOpsRestart or ban URL regex
Application memory (in-process)0.1-1 msServer codePod restart
Distributed cache (Redis, Memcached)1-5 msBackendDEL key or TTL expiry
Database buffer pool0.05-0.5 msDBADB restart

Rule of thumb: the farther away from the user, the longer TTL you can afford, but the harder it is to invalidate.


2. Pick a freshness strategy (not only TTL)

StrategyWhen to chooseHTTP headersExample
Cache forever + fingerprintStatic assets that never change content without changing namemax-age=1y, immutablemain.3b14c8.js
Stale-while-revalidateUser sees data instantly, update in backgroundCache-Control: max-age=300, stale-while-revalidate=86400Stock-price widget
Must-revalidateContent may be stale, but never show older than X without checkingmax-age=600, must-revalidateNews article
No-cacheAlways revalidate with server, keep copy for speedno-cache (NOT no-store)Personalized dashboard
No-storeDo not write to any cache (privacy or legal)no-storeMedical record PDF

3. Choose an invalidation model

  1. TTL only – simplest, good for quarterly releases
  2. Key versioning – change URL → new cache key (v2/endpoint)
  3. Purging API – keep URL, evict on deploy (CloudFront, Azure CDN, Fastly)
  4. Event-driven – DB write → publish event → wipe Redis/CDN key

4. Tune for static-vs-dynamic spectrum

A. “Brochure” site – changes quarterly

  • Cache rules:
    *.html Cache-Control: no-cache (so browser revalidates)
    *.js/css/png max-age=1y, immutable
  • Invalidate only index.html (or rename) on each release
  • Use Service-Worker for offline, but skip runtime caching of API calls (there are none)

B. “Dashboard” app – widgets refresh every 5-30 s

  • Keep shell static (cache 1 year)
  • Each widget hits its own JSON endpoint
  • Endpoints use stale-while-revalidate (SWR) or Server-Sent Events / WebSocket for live data
  • Browser memory cache (SWR libraries: React-Query, SvelteKit load, VueUse, urql, Apollo) keeps last response for zero-jump navigation
  • Redis/CDN caches shared expensive queries (rankings, analytics) with TTL 30-300 s
  • Version the query (/api/v2/chart?interval=1h) so you can roll out new logic instantly

Quick decision cheat-sheet (copy into exam note)

Question to askIf YES → do thisIf NO → do this
Can user tolerate 5-min old data?Set max-age=300Use no-cache or WebSocket
Is asset name hashed?max-age=1y, immutableno-cache
Does data vary per user?CDN: bypass cache OR use Vary: CookieCache at edge with s-maxage
Must work offline?Install Service-Worker, runtime cache API responsesSkip SW, rely on HTTP cache
Deploy <1× per week?TTL only is enoughAdd purge API or key versioning

Traps that kill assessment answers

  1. “no-store” means private – wrong, it means do not write; still readable by anyone who sniffs memory.
  2. 304 Not Modified is slower than 200 from cache – yes, but it is consistently 1 RTT, whereas 200 (from cache) is 0 RTT if resource is still in cache.
  3. CDN hit ratio = performance – not if your TTL is so long that users see stale data and file bug tickets.
  4. “Cache busting query string” (main.js?v=2) – some proxies ignore query string when caching; always change the filename instead.
  5. Forgetting Vary – if you compress, serve WebP, or return localized content, send Vary: Accept-Encoding, Accept-Language or you will poison the cache.