Under the Hood
Networking

CDNs & the edge

You already ship files with a hash in the name — app.4f9c2a.js — and never think about why. This lesson is why. It starts from the one cost no code can beat: the speed of light, which makes an India-to-US round trip ~250ms no matter how fast your server is. A CDN answers by moving copies close to users: anycast puts the nearest edge on the same IP, and caching lets that edge answer without touching your origin at all. Then the machinery that decides what gets cached and for how long — cache keys, Cache-Control, ETag revalidation and the 304 — and the genuinely hard part, invalidation: why purging is slow and global and versioned URLs are instant (that hash in your filename). Finally what you cannot cache (per-user API JSON), edge compute as the middle ground, and signed URLs — serving private media fast without doing auth at the edge.

CDNs & the edge

Look at a filename in your production build: app.4f9c2a8b.js. You've shipped that hash-in-the-name pattern a hundred times, probably without asking why the tooling does it. By the end of this lesson it'll be obvious — it's the clean answer to the single hardest problem in caching, and caching is how the web beats a limit no amount of clever code can touch. That limit showed up in the very first lesson's latency table: a round trip from India to US-East is ~200–250ms, and it is not a software problem.

The wall no code climbs: the speed of light

Light in fiber travels roughly 200,000 km/s — about two-thirds of light in vacuum, because glass slows it. Mumbai to Virginia is about 13,000 km of cable, often more once you account for the routes fiber actually takes. Round that trip — there and back, because a request needs a response — and you're at ~200–250ms of pure propagation delay before you add a single millisecond of server work, TCP handshake, or TLS negotiation. Recall from the pooling and HTTP lessons that a cold connection is several round trips: DNS, TCP, TLS, then the request. Stack those against a 250ms base and a first load from across the planet is comfortably over a second before your backend has done anything at all.

No optimization touches this. You cannot tune a query, cache a computation, or upgrade a CPU to make light cross an ocean faster. There is exactly one move: stop crossing the ocean. Put a copy of the content physically near the user, so the round trip is Mumbai-to-Mumbai (~a few ms) instead of Mumbai-to-Virginia. That is the entire idea of a CDN — a Content Delivery Network — a fleet of servers spread across dozens or hundreds of cities (PoPs, points of presence) that hold copies of your content close to wherever your users are. Distance is the disease; proximity is the only cure.

Anycast: how "nearest" happens without you routing anything

Here's the trick that makes it seamless: a CDN announces the same IP address from every PoP at once. This is anycast. Normally an IP maps to one machine in one place; with anycast, hundreds of machines worldwide all claim 104.18.x.x, and the internet's routing protocol, BGP (Border Gateway Protocol — how networks tell each other "you can reach this range of IPs through me"), naturally carries each user's packets to the topologically nearest PoP announcing that address. A user in Mumbai and a user in São Paulo both fetch the exact same IP; BGP silently delivers each to their local edge. You don't do geo-routing, you don't run different configs per region — the routing fabric does it for free because "shortest path to an announced route" already means "nearest PoP." (Contrast DNS-based geo-routing, which hands out different IPs per region and is coarser and slower to react; anycast steers at the network layer, per packet.)

Caching: how the edge answers without ever calling your origin

Being near the user only helps if the edge can answer — otherwise it just forwards to your distant origin and you've added a hop. The win comes from the edge holding the response and serving it directly. Two questions govern this: what counts as "the same request" (the cache key), and how long a stored copy is allowed to be served (freshness).

The cache key is how the CDN decides a stored response can satisfy a new request. By default it's roughly the method plus the full URL — GET /assets/app.4f9c2a8b.js. Two users hitting that URL get the same cached bytes. This is also the first foot-gun: if a response actually varies by something not in the key — a Cookie, an Accept-Language, an Authorization header — the cache will happily serve one user's version to another unless you tell it to key on that header (the Vary header) or simply don't cache it.

Freshness is controlled by the Cache-Control header your origin sends with the response. The directives you'll actually reach for:

DirectiveMeaning
max-age=31536000A browser may reuse this for N seconds (here, a year) without asking.
s-maxage=3600Overrides max-age for shared caches (the CDN) specifically — the edge keeps it 1 hour even if browsers keep it less.
immutable"This will never change" — the browser won't even revalidate on reload. Pairs with hashed filenames.
no-storeNever cache this, anywhere. For per-user and sensitive responses.
stale-while-revalidate=60Serve the stale copy instantly while fetching a fresh one in the background — the user never waits on the refresh.

When a cached copy's max-age expires, the copy isn't necessarily wrong — just unverified. Rather than re-download it blind, HTTP revalidates. Your origin tags responses with an ETag — a short fingerprint of the content, like ETag: "a1b2c3". When the cached copy goes stale, the cache sends a conditional request with If-None-Match: "a1b2c3". If the content hasn't changed, the origin replies 304 Not Modified — a tiny header-only response, no body — and the cache serves its existing copy with a fresh clock. You paid one small round trip instead of re-downloading a megabyte. That's the same ETag/If-None-Match machinery your browser already uses; a CDN is just another cache in the chain speaking the same protocol.

Invalidation: the genuinely hard part, and why your filenames have hashes

There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton

Caching is easy to turn on and hard to turn off correctly. You've cached logo.png at 300 edges worldwide with a one-year max-age. You ship a new logo. Now what? There are two answers, and the gap between them is the whole lesson.

Purge — tell the CDN "forget logo.png everywhere." It works, but it's the slow, global, error-prone path: the purge has to propagate to every PoP, there's a window where some edges serve old and some serve new, and you have to remember to issue the purge for every changed file, correctly, on every deploy. It's an active, stateful operation you can get wrong — the "cache invalidation is hard" problem in the flesh.

Versioned URLs — the trick that sidesteps invalidation entirely. Instead of changing the contents at a fixed URL, you change the URL whenever contents change. app.js becomes app.4f9c2a8b.js, where the hash is derived from the file's bytes. Edit one character and the hash changes, so the URL changes, so it's a brand-new cache key the CDN has never seen — it fetches fresh, no purge required. The old URL still sits cached and harmless; nothing references it anymore. And because a hashed URL's content can never change (a change would mint a different URL), you can slap Cache-Control: max-age=31536000, immutable on it and cache it essentially forever. You already ship this on every build. Now you know it's not a bundler quirk — it's how frontend tooling converts the hard problem (invalidate the right things at the right time) into the easy one (just use a new name), exactly the "naming things" side of Karlton's joke doing the "cache invalidation" side's job.

What you can't cache — and the two escape hatches

The whole scheme assumes a response that's the same for everyone: your JS bundle, CSS, fonts, images, a public marketing page. That's what CDNs were built for. The opposite — per-user, per-request data — is exactly what you must not cache at a shared edge:

So dynamic, personalized content seems stuck making the full trip to origin. Two things narrow the gap:

Edge compute is the middle ground: small functions that run at the PoP instead of at your origin — Cloudflare Workers, Lambda@Edge, and friends. Personalization, auth checks, A/B splits, request rewriting, even assembling a page from cached fragments can happen near the user, so only the genuinely origin-bound part crosses the distance. It's "put the logic at the edge too," not just the static files.

Signed URLs solve a sharp sub-problem: serving private files (a user's uploaded photo) fast, from the CDN, without running an auth check at the edge on every request. The idea is a time-limited capability. Your origin, which can check auth, generates a URL with a cryptographic signature and an expiry baked into the query string — https://cdn.example.com/media/abc.jpg?Expires=1720000000&Signature=.... The CDN doesn't know or care who you are; it just verifies the signature is valid and unexpired and serves the bytes. Possession of the signed URL is the permission — the auth decision was made once, at signing time, by the server that could make it, and encoded into the link. After it expires the same URL is dead. You get edge-speed delivery of private content without teaching the edge how to authenticate, and without making files public.

Go deeper

Check yourself

Answer out loud, as if an interviewer asked. If you hand-wave, reread that section.

  1. A colleague proposes moving your API servers to a faster CPU tier to cut the ~250ms your India users see loading a page from your US origin. Why will that barely help, and what is the only category of fix that addresses the real cost?
  2. A CDN announces one IP from 200 cities and a user in Mumbai is served by the Mumbai PoP without you configuring any geo-routing. Name the addressing scheme and the routing protocol that make that happen, and what each contributes.
  3. A cached asset's max-age has expired but the file has not actually changed. Walk through what the ETag and If-None-Match headers let the cache do, and why a 304 is far cheaper than re-fetching.
  4. You ship a new logo. Explain why renaming it to logo.8fa3c1.png makes it appear instantly worldwide with no purge, and what specifically goes wrong if you instead overwrite logo.png in place and rely on purging.
  5. An engineer adds Cache-Control: max-age=60 to GET /me to speed it up, and users start seeing each other's profiles. Explain the exact mechanism, and what the cache key would have to include (or what directive it should use) to be safe.
  6. You want a user's private photo to load fast from the CDN, but the CDN cannot check your app's login tokens. How does a signed URL let the edge serve private content correctly, and where was the authorization decision actually made?