Master HTTP caching headers, CDN strategy, service worker caching, stale-while-revalidate, browser storage, and API response caching. Know exactly when to use each caching layer and how to answer every caching question in a frontend system design interview.
Caching is a series of progressively closer copies of your data. The origin server has the source of truth. The CDN has a regional copy. The browser has a local copy. The service worker has an offline copy. Each layer trades freshness for speed — the closer the cache, the faster the response. Your job as a frontend architect is to decide how stale is acceptable and for how long.
The fastest cache — assets already downloaded and held in browser memory for the current session. Zero network requests. Evicted when the browser tab closes.
The HTTP cache stores responses based on Cache-Control headers. This is the most important cache layer to understand.
# Static assets with content hashes — cache forever
Cache-Control: public, max-age=31536000, immutable
# ^ bundle.abc123.js — if the hash is in the filename, the URL changes on update
# HTML pages — always revalidate
Cache-Control: no-cache
# ^ "no-cache" does NOT mean "don't cache" — it means "revalidate before using"
# ^ "no-store" means "never cache" — completely different
# API responses — short cache
Cache-Control: public, max-age=60, stale-while-revalidate=300
Key directives: max-age = serve from cache for N seconds. stale-while-revalidate = after max-age, serve stale while fetching fresh in background. immutable = never revalidate (for content-hashed files). no-store = never cache. private = CDN cannot cache, only browser.
A JavaScript worker that intercepts network requests and can respond from a programmable cache. Enables offline support and fine-grained caching strategies per resource type.
// Service worker — cache-first strategy for static assets
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image') {
event.respondWith(
caches.match(event.request).then((cached) =>
cached ?? fetch(event.request).then((response) => {
caches.open('images-v1').then((cache) => cache.put(event.request, response.clone()));
return response;
})
)
);
}
});
Common strategies: Cache-first (static assets), Network-first (API calls), Stale-while-revalidate (semi-dynamic), Network-only (auth), Cache-only (offline fallback).
A globally distributed network of proxy servers that cache responses at edge locations near users. The CDN respects Cache-Control: public directives from your origin server. Reduces latency from 200ms (cross-continent) to 10ms (local PoP).
Cache invalidation: The hardest problem. Strategies: content hashing (file.abc123.js, never needs invalidation), cache-busting query params (?v=2), and CDN purge APIs for emergency invalidation.
The browser (or CDN) serves the stale cached response immediately, then revalidates in the background. The user sees instant response; the cache is fresh for the next request.
Cache-Control: max-age=60, stale-while-revalidate=300
# Serve from cache for 60s. Between 60-300s, serve stale + revalidate.
# After 300s: wait for network response.
// React Query implements the same pattern at the app level
const { data } = useQuery({
queryKey: ['posts'],
staleTime: 60_000, // treat as fresh for 60s
gcTime: 300_000, // keep in memory for 5 min after unmount
});'no-cache' means don't cache — it actually means 'revalidate before using the cached copy'. To prevent caching entirely, use 'no-store'.
CDNs only cache static files — CDNs can cache API responses too, using Cache-Control: public. This is how edge caching for API routes works in Vercel/Cloudflare.
Service workers are just for offline apps — they're a powerful performance tool. Caching static assets in a service worker means zero-network loads for returning users even on fast connections.
Cache invalidation is easy — it's notoriously hard. Content hashing (embedding a file hash in the filename) is the most reliable solution: the URL itself changes when the file changes, so old caches are never served stale.
Next.js static assets: _next/static/* served with Cache-Control: public, max-age=31536000, immutable — the hash in the filename guarantees freshness without CDN invalidation
React Query / SWR: stale-while-revalidate at the application level — components show cached data instantly, background refetch updates the UI when fresh data arrives
Cloudflare Workers: running JavaScript at the edge, combining CDN caching with dynamic logic — serve cached HTML for anonymous users, bypass cache for authenticated sessions
GitHub's API uses ETag-based conditional requests — `If-None-Match: etag-value`, server returns 304 Not Modified saving bandwidth when data hasn't changed
What is the Cache-Control header and what are its key directives?
What is stale-while-revalidate and why is it a good default for HTML pages?
What is content-based cache busting and how does it work?
What caching strategies can a service worker implement?
What is ETags and conditional requests, and when does the browser use them?
What is cache invalidation and why is it considered hard?
Reading answers is not the same as knowing them. Practice saying them out loud with AI feedback — that's what builds real interview confidence.