Loading...
Loading...
Browser, CDN, Redis, Service Workers, and client-side API caching — every layer SDE-2 interviews cover.
Multi-layer caching from browser to database: HTTP headers, CDN, Redis, cache patterns (cache-first, stale-while-revalidate), browser storage APIs, and React Query / SWR client caching.
Multi-layer caching from browser HTTP cache to CDN, application Redis, and database query cache — plus cache-first, network-first, stale-while-revalidate patterns, browser storage APIs, and React Query / SWR client caching.
Caching is one of the highest-leverage frontend performance techniques — it reduces load times, server load, and improves scalability across every layer of the stack.
This module covers the four caching layers, six core cache strategies, browser storage options, API caching with TanStack Query and SWR, and the best practices SDE-2 interviews expect.
You will understand cache invalidation trade-offs, when stale-while-revalidate beats cache-first, how CDN edge caching interacts with Cache-Control headers, and how to avoid serving stale or sensitive data to the wrong user.
Layers covered: browser HTTP cache, CDN, application cache (Redis), database query cache, Service Worker strategies, and client libraries like TanStack Query — each with headers, patterns, and interview comparisons.
Every SDE-2 frontend interview touches caching: Cache-Control headers, CDN edge caching, Service Worker strategies, and client-side data libraries. Wrong cache policy means stale data, security leaks, or slow pages. Knowing when to invalidate vs serve stale data is as important as knowing how to cache.
Store copies of data closer to the user for faster access — SDE-2 frontend essential.
Caching stores copies of files or data in temporary storage so they can be accessed more quickly on subsequent requests. Every layer of the stack — browser, CDN, application server, database — can cache.
Interactive · Cache layer flow
Browser cache
—
CDN edge
—
App server
—
Database
—
Four layers — each closer to the user means lower latency.
HTTP cache for HTML, CSS, JS, images, API responses
Edge servers worldwide — static assets and cacheable HTML/API
Redis / Memcached — rendered pages, fragments, computed data
Query result cache, indexes — reduces DB round-trips
Interview framing
Cache-Control, ETag, and Last-Modified — the headers that control client-side caching.
# Static assets (images, CSS, JS) — cache 1 year Cache-Control: public, max-age=31536000 # HTML pages — revalidate every time Cache-Control: no-cache # Sensitive data — never store Cache-Control: private, no-store # Validate with ETag ETag: "abc123" Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT
| Header | Value | Behavior |
|---|---|---|
| Cache-Control | max-age=3600 | Cache for 3600 seconds (1 hour) |
| Cache-Control | no-cache | Store but revalidate before using |
| Cache-Control | no-store | Do not cache at all |
| Expires | 2026-12-31 23:59:59 GMT | Hard expiration date/time |
| ETag | "abc123" | Version identifier for conditional requests |
| Last-Modified | Wed, 21 Oct 2026 ... | Resource last modified time |
no-cache vs no-store
Geographically distributed edge caches — lower latency, higher throughput.
CDNs store copies of static assets and cacheable responses at edge locations close to users. The browser talks to the nearest edge node instead of your origin server — cutting round-trip time and absorbing traffic spikes.
Redis and Memcached — page, fragment, and data caching at the server.
// Redis cache-aside pattern const user = await redis.get("user:123"); if (!user) { const fresh = await db.query("SELECT * FROM users WHERE id = 123"); await redis.set("user:123", JSON.stringify(fresh), { EX: 3600 }); return fresh; } return JSON.parse(user);
Query result cache and indexes — reduce expensive DB reads.
-- Indexing (automatic lookup optimization) CREATE INDEX idx_user_email ON users(email); -- Check query cache settings (MySQL) SHOW VARIABLES LIKE "query_cache%";
Six patterns — pick based on freshness requirements and offline needs.
1. Cache First (Cache-Then-Network)
Use case: Static assets (images, CSS, JS) that rarely change
async function cacheFirst(url) { const cached = await caches.match(url); if (cached) return cached; const response = await fetch(url); const cache = await caches.open("my-cache"); cache.put(url, response.clone()); return response; }
2. Network First (Network-Then-Cache)
Use case: Dynamic content — API responses, user-specific data
async function networkFirst(url) { try { const response = await fetch(url); const cache = await caches.open("my-cache"); cache.put(url, response.clone()); return response; } catch { const cached = await caches.match(url); return cached ?? new Response("Offline", { status: 503 }); } }
3. Stale-While-Revalidate
Use case: Content tolerating slight staleness — blog posts, news, catalogs
async function staleWhileRevalidate(url) { const cached = await caches.match(url); // Refresh in background fetch(url).then(async (response) => { const cache = await caches.open("my-cache"); cache.put(url, response.clone()); }); return cached ?? fetch(url); }
4. Time-Based Expiration (TTL)
Use case: Session data, user preferences, API responses with known freshness window
const store = {}; function getWithTTL(key, ttlMs = 3_600_000) { const entry = store[key]; if (entry && Date.now() - entry.timestamp < ttlMs) { return entry.data; } const data = fetchFromDatabase(key); store[key] = { data, timestamp: Date.now() }; return data; }
5. Write-Through Caching
Use case: Data needing consistency — user profiles, inventory counts
async function writeThrough(key, value) { await cache.set(key, value); await db.save(key, value); }
6. Cache Busting
Use case: API responses or data structures that change shape
const version = "v1"; const key = `${version}:product:123`; // On schema change, bump version const newKey = "v2:product:123"; await cache.set(newKey, newValue);
Cache API, IndexedDB, LocalStorage, SessionStorage, and Cookies.
| Storage | Capacity | Sync/Async | Use Case | Lifetime |
|---|---|---|---|---|
| Cache API | Large (quota-based) | Async | Service Worker network cache | Persistent |
| IndexedDB | ~50–100 MB+ | Async | Large structured data, offline apps | Persistent |
| LocalStorage | ~5 MB | Sync | Small key-value, user preferences | Persistent |
| SessionStorage | ~5 MB | Sync | Temporary form state, tab-scoped data | Session only |
| Cookies | ~4 KB | Sync | Auth tokens, session IDs | Expiration-based |
// Cache API (Service Workers) const cache = await caches.open("v1"); await cache.add("/api/products"); const response = await cache.match(request); await caches.delete("v1"); // IndexedDB (large structured data) const db = await openDB("my-db", 1, { upgrade(db) { db.createObjectStore("products", { keyPath: "id" }); }, }); await db.put("products", { id: 1, name: "Product" }); const product = await db.get("products", 1); // LocalStorage (simple sync KV) localStorage.setItem("user", JSON.stringify({ id: 1, name: "John" })); const user = JSON.parse(localStorage.getItem("user") ?? "null");
When to use which
Client-side in-memory caching with background revalidation.
TanStack Query (React Query)
import { useQuery } from "@tanstack/react-query"; const { data, isLoading, isError } = useQuery({ queryKey: ["users"], queryFn: fetchUsers, staleTime: 5 * 60 * 1000, // 5 min — data considered fresh gcTime: 30 * 60 * 1000, // 30 min — keep in cache after unmount retry: 3, });
SWR
import useSWR from "swr"; function Profile() { const { data, error } = useSWR("/api/user", fetcher, { refreshInterval: 5000, // poll every 5s dedupingInterval: 2000, // dedupe requests within 2s }); }
What to cache, what to avoid, and how to invalidate correctly.
| ✅ Do | Why |
|---|---|
| Cache only GET requests | POST mutates state — responses should be fresh |
| Set reasonable TTLs | 1 hour for API, 1 year for hashed static assets |
| Monitor hit/miss rates | Tune policies based on real data |
| Use cache versioning | v1:product:123 → v2:product:123 on schema change |
| Document what's cached | Where, why, TTL — for team clarity |
| ❌ Don't | Problem |
|---|---|
| Cache POST without design | Data inconsistency, wrong mutations |
| Infinite TTL on dynamic data | Stale data forever |
| Cache user data globally on CDN | Security risk — wrong user sees data |
| Skip cache invalidation | Stale data persists after updates |
| Over-cache everything | Memory waste, harder debugging |
Sensitive data rule
Cache-Control: private, no-store and httpOnly cookies for session management.Common interview questions about caching strategies.