Frontend Caching Strategies
System design for frontend caching — HTTP, CDN, Service Worker, memory stores, and invalidation tradeoffs.
- system-design
- interview
- architecture
- caching
Scope the problem
Caching is layered. Interviews fail when candidates only say “use Redis” for a frontend question. Cover what is cached, where, and how it invalidates.
In scope: static assets, API data on client, HTML documents, SW, memory caches.
The layers
┌────────────────────────────────────────────┐
│ In-memory (React Query / store) │
├────────────────────────────────────────────┤
│ Service Worker Cache Storage │
├────────────────────────────────────────────┤
│ HTTP browser cache │
├────────────────────────────────────────────┤
│ CDN / edge cache │
├────────────────────────────────────────────┤
│ Origin │
└────────────────────────────────────────────┘
Each layer has different keys, TTLs, and privacy.
Static assets (JS/CSS/images)
| Strategy | Header idea |
|---|---|
| Fingerprinted bundles | Cache-Control: public, max-age=31536000, immutable |
| HTML entry | no-cache or short + revalidate |
| Images via CDN | long cache + versioned URL or query hash |
Never long-cache app.js without hash — users stick on broken deploys.
HTML documents
| App type | HTML caching |
|---|---|
| Marketing SSG | long + CDN ISR |
| Personalized app shell | private, no-store or short |
| Hybrid | cache shell; client fetch user data |
API data (client memory)
React Query / SWR patterns:
staleTime: 60_000, // serve memory without refetch
gcTime: 5 * 60_000,
// invalidateQueries on mutation
| Pattern | Behavior |
|---|---|
| cache-first | instant UX; risk stale |
| network-first | fresher; slower |
| stale-while-revalidate | best default for many GETs |
Keys must include tenant, user, and params (multi-tenant).
Service Worker strategies
| Resource | Strategy |
|---|---|
| App shell | cache-first |
| API GET list | network-first or SWR |
| POST/PUT | network-only + outbox offline |
| Images | cache-first with limit LRU |
Precache only shell; runtime cache carefully with versioned cache names (app-shell-v3).
CDN / edge
s-maxagefor shared cachesstale-while-revalidate/stale-if-error- Purge on publish (tag-based purge ideal)
- Respect
Authorization— don’t cache personalized responses publicly
Invalidation map
| Change | How users see it |
|---|---|
| New frontend deploy | new hashed assets; HTML revalidated |
| CMS content publish | purge tag / ISR revalidate |
| User mutates entity | client invalidateQueries; optional SW message |
| Feature flag kill | short TTL flags bootstrap |
Hardest: HTML + API + SW all holding different generations — design release so HTML points only to new asset hashes (atomic deploy).
Correctness hazards
- Caching authenticated responses on shared CDN
- SW serving old API schema after backend deploy
- Memory cache without tenant id
max-ageon HTML with inline config that must be fresh
Performance vs freshness matrix
| Data | Freshness need | Cache |
|---|---|---|
| Logo | low | long CDN |
| Product PDP | medium | ISR minutes |
| Stock count | high | short / on-demand |
| Chat messages | realtime | memory + WS |
Observability
- CDN hit ratio
- Client cache hit (custom)
- Stale content complaints → correlate with TTL
Tradeoffs
- Aggressive SW vs confusing updates (need update UX)
- Long staleTime vs multi-tab inconsistency — broadcast invalidation
- Edge personalization vs hit ratio (edge rendering)
Interview close
Walk layers top-down for a specific product (e.g. news site): hashed assets immutable, HTML ISR, article JSON SWR in client, SW shell offline. State invalidation on publish and on user mutation. Call out auth cache dangers.
End-to-end example: product page
| Resource | Cache |
|---|---|
/products/sku HTML |
CDN ISR 60s + SWR |
product-abc.js |
immutable year |
GET /api/product/sku |
browser memory 30s; CDN optional short |
| Review widget JS | immutable; load deferred |
| User-specific price | private, no-store |
Debugging stale UI
- Check HTML generation time header
- Check SW cache name / version
- Check React Query devtools freshness
- Hard reload vs normal reload differences
Teach support a “disable SW” snippet for triage.
Interview close add-on
Emphasize auth + CDN as the #1 footgun and hashed assets + short HTML as the #1 default win.
Related on this site
- Caching Static Assets Fingerprinting
- PWA Install and Offline Shell
- Edge Rendering Tradeoffs
- System Design Interview Framework