CDNs and Edge Caching
The cheapest performance improvement available to any global product. How CDNs work, the HTTP headers that control them, and how to invalidate content across hundreds of locations.
Covers: How a CDN works, cache-control headers, ETags, purging, push vs pull, edge compute, origin shielding
A CDN is a cache with geography. It solves a problem no amount of server optimisation can touch: the speed of light. If your origin is in Virginia and your user is in Singapore, the round trip is ~230 ms no matter how fast your code is. A PoP in Singapore turns that into ~10 ms.
30-second answer
A CDN is a network of points of presence worldwide. DNS or anycast routing directs a user to the nearest PoP; if that PoP has a fresh copy it serves it in tens of milliseconds, and if not it fetches from origin, caches it, and serves subsequent requests locally. Put static assets, images, video, downloads and cacheable API responses through it. Keep personalised, authenticated and rapidly changing content on the origin - or use edge compute so the dynamic part is assembled near the user while the static shell still comes from cache.
1 · Routing to the nearest PoP
Either GeoDNS returns a different IP per region, or - more commonly now - a single anycast address is announced from every PoP and BGP naturally routes to the topologically nearest one. Anycast also absorbs DDoS traffic by spreading it across the whole network.2 · Cache lookup at the edge
The PoP checks its local cache using the cache key: usually scheme + host + path + selected query parameters + anyVaryheaders. Getting the cache key right is most of CDN configuration - including a random analytics parameter fragments your cache into millions of useless entries.3 · Miss → origin fetch
On a miss the PoP fetches from origin, ideally through a shield PoP (see below), stores it per the response headers, and serves it.4 · Subsequent requests
Served from the edge at 10–30 ms with zero origin load. A well-configured CDN offloads 90–99% of requests for a media-heavy site.
| Content | CDN? | Notes |
|---|---|---|
| JS/CSS bundles | Yes - immutable | Hash the filename and cache for a year |
| Images and video | Yes | The single biggest bandwidth and cost win |
| Public API GET responses | Often | Short TTL plus stale-while-revalidate |
| Rendered marketing pages | Yes | Purge on publish |
| Authenticated user pages | Rarely | Only with Vary on the auth cookie, and usually private |
| POST/PUT/DELETE | Never | Non-idempotent; must reach origin |
| Real-time prices, inventory | No | Or a TTL of a few seconds with explicit purge |
Push versus pull
| Pull (origin pull) | Push | |
|---|---|---|
| Population | Lazily, on first request | You upload in advance |
| First request | Slow (a miss) | Fast |
| Storage cost | Only what is requested | Everything, everywhere |
| Good for | Long-tail catalogues, most websites | Large launches, video libraries, predictable hot content |
Say these points
- Anycast or GeoDNS routes users to the nearest PoP; the edge caches per response headers.
- The cache key determines hit rate - strip irrelevant query parameters.
- Origin shielding turns N PoP misses into one origin request per region.
- Static and media content are the obvious wins; personalised content usually is not.
- Pull is the default; push suits predictable, large, hot content.
Avoid these mistakes
- Including tracking parameters in the cache key and destroying hit rate.
- Caching authenticated responses without a correct `Vary`, leaking one user's data to another.
- No origin shielding, so a purge produces a stampede from every PoP.
- Assuming a CDN helps with writes - it does not.
Expect these follow-ups
- How would you serve personalised content and still get a high cache hit rate?
- What is the risk of `Vary: Cookie`?
- How does a CDN help with DDoS absorption?
30-second answer
`Cache-Control` is the primary control: `max-age` for browsers, `s-maxage` for shared caches, `public` versus `private`, `no-store` to forbid caching entirely, and `stale-while-revalidate` to serve stale content while refreshing in the background. `ETag` and `Last-Modified` enable revalidation, letting the server return a 304 with no body. For invalidation, the best strategy is to avoid it: use content-hashed immutable URLs so a new version is a new URL. When you must purge, prefer surrogate keys - tag responses and purge by tag - over purging URLs one at a time.
| Directive | Meaning | Typical use |
|---|---|---|
max-age=N | Fresh for N seconds in any cache | Browser lifetime |
s-maxage=N | Fresh for N seconds in shared caches only | Long at the CDN, short in the browser |
public | Any cache may store it | Static assets |
private | Browser only, never a shared cache | Personalised HTML |
no-cache | Store, but revalidate before each use | Frequently changing but revalidatable |
no-store | Never store anywhere | Auth tokens, payment pages |
immutable | Never revalidate within max-age | Content-hashed bundles |
stale-while-revalidate=N | Serve stale up to N s while refreshing in background | The single best directive for API responses |
stale-if-error=N | Serve stale for N s if the origin errors | Availability insurance |
# 1. Immutable asset (filename contains a content hash)
Cache-Control: public, max-age=31536000, immutable
# app.4f3a91c.js changes => new filename => new URL. Invalidation never needed.
# 2. HTML shell: always revalidate, but let the CDN hold it briefly
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=600
ETag: "a91c4f3"
# Browser revalidates each load (cheap 304); CDN serves for 60 s and refreshes
# in the background for 10 more minutes rather than making users wait.
# 3. Personalised or sensitive
Cache-Control: private, no-storeRevalidation: ETag and Last-Modified
# First response
HTTP/1.1 200 OK
ETag: "a91c4f3"
Cache-Control: max-age=0, must-revalidate
# Later request
GET /api/config
If-None-Match: "a91c4f3"
# Unchanged: no body transferred
HTTP/1.1 304 Not Modified
ETag: "a91c4f3"Note what a 304 does and does not save. It saves bandwidth, not the round trip - the client still pays full latency to ask. For a 2 KB JSON response over a 150 ms link, revalidation saves almost nothing. For a 2 MB image it saves a great deal. Choose accordingly.
Invalidation, from best to worst
1 · Change the URL (best)
Content-hashed filenames mean a new version is a new URL that was never cached. There is nothing to invalidate. Combine withimmutable, max-age=31536000.2 · Surrogate keys / cache tags
Tag each response (Surrogate-Key: product-42 category-shoes) and purge by tag. Updating product 42 purges every page referencing it in one call - the only approach that scales to a real content site.3 · Purge by URL
Works, but you must know every affected URL, including every query-parameter variant. Fine for a handful of pages, unmanageable for thousands.4 · Purge everything (worst)
Empties the cache globally and sends every subsequent request to origin. Without shielding this is a self-inflicted outage. Reserve it for genuine emergencies.
Say these points
- `s-maxage` targets shared caches; `max-age` targets browsers - set them differently.
- `stale-while-revalidate` removes expiry-driven latency spikes and edge stampedes.
- ETag revalidation saves bandwidth but not the round trip.
- Content-hashed immutable URLs make invalidation unnecessary.
- Surrogate keys are the only invalidation strategy that scales; purge-all is an emergency tool.
Avoid these mistakes
- Long `max-age` on a URL whose content can change.
- Serving personalised content as `public`.
- Relying on a purge for anything security-sensitive.
- Purging everything on each deploy and hammering the origin.
Expect these follow-ups
- How do you cache a page that is identical for everyone except a username in the header?
- What is the correct cache configuration for a versioned public API?
- How would you serve an A/B test through a CDN without halving your hit rate?
Showing 2 of 2 questions for cdn-and-edge-caching.
Check your understanding
4 questions · no sign-up, nothing stored
Hands-on challenge
Build it - this is what you talk about in a deep-dive round.
Design the caching layer for a news site
A news site serves 50 million page views a day globally. Articles are updated after publication, and the homepage changes every few minutes.
Requirements
- Specify Cache-Control for: hashed assets, images, the homepage, an article page, and a logged-in user's page.
- Design the cache key, listing which query parameters are included and which are stripped.
- Define the surrogate-key scheme and show how correcting one article purges every page that references it.
- Explain how a breaking-news homepage update propagates in under 10 seconds without hammering origin.
- Estimate the origin QPS with and without the CDN, assuming a 97% offload rate.
Stretch goals
- Serve a personalised header on a cached page using edge compute, and state the hit-rate impact.
- Design the failure behaviour when the origin is completely down, using `stale-if-error`.