CDN Caching
How CDN edge caching works: cache headers (Cache-Control, ETag), cache keys, purging strategies, and dynamic content caching.
What Is a CDN?
A Content Delivery Network (CDN) is a globally distributed network of edge servers (Points of Presence, or PoPs) that cache content close to users. When a user in Tokyo requests a file from a US-based origin server, without a CDN the request crosses the Pacific and back — adding 150+ ms of latency. With a CDN, the request hits a Tokyo edge node that serves the cached file in under 10 ms.
CDNs like CloudFront (AWS), Fastly, Cloudflare, and Akamai are the outermost cache layer in most architectures. They reduce origin load, improve global performance, and provide DDoS mitigation as a side effect. Understanding how CDN caching works — and its failure modes — is essential for designing high-traffic web systems.
Cache-Control Headers
The CDN's caching behavior is controlled by HTTP headers returned by your origin server. `Cache-Control` is the primary directive:
- `Cache-Control: max-age=86400` — cache for 24 hours from the request time
- `Cache-Control: s-maxage=3600` — like `max-age` but applies only to shared caches (CDN); browsers use `max-age`
- `Cache-Control: no-cache` — must revalidate with origin before serving (not 'don't cache')
- `Cache-Control: no-store` — never cache (for sensitive data like bank account pages)
- `Cache-Control: stale-while-revalidate=60` — serve stale content for up to 60s while fetching fresh content in background
- `Cache-Control: private` — only browser may cache; CDN must not store it
ETag is a validation header — a hash or version identifier of the response. On subsequent requests, the browser sends `If-None-Match: <etag>`. If content hasn't changed, the origin returns `304 Not Modified` with no body, saving bandwidth. CDNs use ETags for cache revalidation with the origin.
Cache Keys and Vary
A CDN determines whether to serve a cached response by matching the cache key — by default, the URL path + query string. Two requests for `GET /product?id=42` share the same cache entry. But requests can also vary by other dimensions, and the `Vary` header tells the CDN which request headers create distinct cache entries:
- `Vary: Accept-Encoding` — cache separate versions for gzip/br/identity (almost always correct)
- `Vary: Accept-Language` — cache per language (can cause cache fragmentation)
- `Vary: Cookie` — cache per cookie value (usually wrong for CDN; causes effectively zero caching)
Avoid Vary: Cookie at the CDN
`Vary: Cookie` means every unique cookie value gets its own cache entry. Since most users have unique session cookies, this defeats CDN caching entirely. Instead, strip cookies in CDN config for publicly cacheable paths, or use separate domains/paths for authenticated content.
Cache Purging Strategies
When content changes at the origin, you need to invalidate CDN caches. Several strategies exist:
| Strategy | How It Works | Pros | Cons |
|---|---|---|---|
| TTL Expiry | Wait for cache entries to expire naturally | Zero operational complexity | Serves stale content until TTL expires |
| Purge by URL | Send purge API call for specific URLs | Precise, immediate | Must know every cached URL; can be many calls |
| Surrogate Keys (Cache Tags) | Tag responses with content IDs; purge by tag | Purge all variants of a resource atomically (Fastly, Cloudflare) | Must implement tagging in origin |
| Cache Busting (versioned URLs) | Embed content hash in URL (e.g., `main.a3f9b.js`) | Infinite TTL; perfect for immutable assets | URL changes on every deploy; requires build pipeline |
Dynamic Content Caching
CDNs are not only for static assets. Modern CDNs can cache API responses and HTML pages if given the right headers. Techniques for caching 'dynamic' content:
- Edge Side Includes (ESI): compose a cached page from fragments, some static (nav bar, header) and some dynamic (shopping cart). Fastly and Varnish support ESI.
- Stale-While-Revalidate: serve the stale cached version immediately, revalidate in background. Users always get fast responses; eventual consistency.
- Short TTL caching: cache an API response for 5 seconds. Under 100k RPS, this turns 100k origin requests into ~1 every 5 seconds — a 500,000x reduction.
- Segment by authentication: use `s-maxage` for public endpoints, `Cache-Control: private` for authenticated responses. Strip auth cookies on CDN for public paths.
Real-world example: Cache-busting for static assets
At deploy time, webpack/Vite appends a content hash to JS/CSS filenames: `app.a3f9b2.js`. These files are served with `Cache-Control: max-age=31536000, immutable` — a one-year TTL. Since the URL changes on every deploy, old URLs can never serve stale new code. New URLs start cold and fill the CDN cache on first hits globally. This is the gold standard for static asset delivery.
Interview Tip
CDN questions appear in system design interviews when you're designing a media platform, e-commerce site, or any global service. Key points: (1) Set `s-maxage` on public API responses, even short ones — 5-60 seconds can be transformational at scale. (2) Use cache-busting (versioned URLs) for immutable static assets with long TTLs. (3) Always mention cache invalidation strategy — what happens when you push an update? (4) Authenticate via separate domain or strip cookies so CDN can cache public content.