OwlTheEngineer
How It Actually WorksEpisode 10 · System design

How CDNs work: distance is latency you can't optimise away

A CDN is not a faster server. It's a copy of your response nearer the person asking — and everything that surprises you about it follows from what HTTP lets a shared cache keep.

Owl · 5 min read · 0 views
– comments
Ep 10 · System design
60s VISUAL BREAKDOWN
Episode 10
Watch the fast visual breakdown or discuss on social:

Your server answers in ten milliseconds. Someone on the other side of the world waits a third of a second for the same page, and every profiler you point at the problem says the handler is fine. It is fine. Almost none of that gap is your code — it’s the wire, and the only thing you can do to a wire is make it shorter. That’s the whole idea behind a CDN, and the first misunderstanding people carry about it.

Distance, in milliseconds you cannot refactor

Light in fibre covers about 204 km per millisecond (a standard single-mode fibre has a group index of ~1.468 against light’s exact 299,792.458 km/s in vacuum). Nine thousand kilometres — an ordinary intercontinental hop — out and back is:

18,000 km ÷ 204 km/ms = 88 ms

Eighty-eight milliseconds before anyone does any work, and that’s a floor, not an estimate: real paths detour around oceans and peering arrangements, and every router adds its own microseconds. A page is also not one request — it’s a document plus stylesheets plus scripts plus images — so that round trip gets paid on connection setup and again on anything the browser can’t ask for early. There’s no index for this, no cache layer inside your process. There is only less distance.

A CDN is not a faster server

The mental model that causes the most bugs: a CDN is somewhere you upload your site to. It isn’t. A CDN edge is a gateway — a reverse proxy that “acts as an origin server for the outbound connection but translates received requests and forwards them inbound to another server” (RFC 9110 §3.7) — sitting in front of a shared cache, one that “stores responses for reuse by more than one user” (RFC 9111 §1), as opposed to your browser’s private cache, dedicated to you alone.

Nothing is pushed to it. It holds nothing until a request misses, and what it may keep afterwards is decided entirely by headers your origin sent. A CDN is your caching policy executed at a hundred addresses — if your policy is “say nothing and hope,” that’s what gets executed. Keep the private/shared split in mind: they obey different directives, and only one of them has ever heard of your purge button.

What the origin says the edge may keep

The edge holds a copy for as long as your response said it may — RFC 9111 §4.2: response_is_fresh = (freshness_lifetime > current_age).

Cache-Control: public, max-age=60, s-maxage=600
Age: 37

max-age is the number for private caches; s-maxage overrides it for shared caches. A response 37 seconds old still has 563 seconds of edge freshness left under s-maxage=600, while your browser applies max-age=60 and calls it stale in 23 — same bytes, two expiry times, on purpose.

Say nothing at all, and a cache is allowed to guess: RFC 9111 §4.2.2 lets it assign a heuristic freshness — commonly 10% of the time since Last-Modified. A page last changed 100 days ago can be considered fresh for ten days by a cache you never configured. The fix is to always send an explicit directive, including the ones that mean no — and know which one you mean:

DirectiveWhat it actually says
no-cacheMust be revalidated before reuse. It may still be stored.
no-storeMust not be stored at all — the one people mean when they say “don’t cache.”
privateA shared cache must not store it; your browser still may.
immutableContent at this URL will not change — never revalidate, not even on reload.

The mistake you cannot take back

You’ve just learned long TTLs are good, so you put this on your HTML document:

Cache-Control: public, max-age=31536000

A year. Then you ship a fix. Every edge is purgeable — that’s a button the network gives you. Every browser that already fetched that document is not. A private cache has no purge API, no support ticket. RFC 9111 §4.4 is the only invalidation HTTP defines, and it triggers only when an unsafe request passes through that cache to the same URI — there is no mechanism by which an origin tells a browser it has never spoken to that a stored response is now wrong. For the next twelve months, some fraction of your users hold a document that’s fresh by the rules and points at the old everything. They are not broken. They are correct, and stuck.

The pairing that makes long TTLs safe:

What it isTTL
A URL whose content can change — HTML, JSON APIsshort, or no-cache
A URL whose content can never change — /assets/app.9f2c4c1e.jsa year, plus immutable

Long TTLs belong only on content-addressed URLs. The document that links to them is the one thing you have to keep changeable.

Invalidation: the only trick that always works

Every CDN has a purge API. None of them are HTTP — RFC 9111’s only invalidation is §4.4, and the IANA HTTP Method Registry has no PURGE. Purge is a proprietary control plane; its semantics and propagation time are your provider’s, not the standard’s.

RFC 9111 §4 defines the cache key as “composed from, at a minimum, the request method and target URI.” A new URI cannot hit an old entry — that’s not policy, it’s arithmetic, which is why content-hashed filenames beat every invalidation strategy that depends on someone else obeying a message. You don’t ask the cache to forget. You ask for something it’s never heard of, and the old entry ages out unread.

Revalidation, and what an ETag actually promises

Stale doesn’t mean wrong — it means unverified. When a copy goes stale, the edge asks instead of re-downloading:

GET /products/42 HTTP/1.1
If-None-Match: "9f2c4c1e"
HTTP/1.1 304 Not Modified
ETag: "9f2c4c1e"

A 304 carries no body — the round trip is still paid, but the bytes aren’t, and the stored copy is refreshed with the new headers. stale-while-revalidate (RFC 5861) takes this further: for a set window after going stale, a cache may serve the old copy immediately and revalidate in the background, so nobody waits on the origin at all.

What a CDN cannot help with — and the one thing that survives

Some responses were never shareable — private, no-store, an Authorization header, or Vary: Cookie, which makes every cache entry effectively per-user. For those, the copy near you buys nothing.

Except one thing: the connection is still terminated near you. A first request pays a TCP handshake plus a TLS handshake plus the request itself — three round trips, at 88 ms each, before a single byte of an uncacheable, personalised response moves. Terminate both handshakes at an edge 10 ms away instead, while the edge holds a warm connection back to your origin, and two of those three round trips shrink by an order of magnitude. That’s the part of “use a CDN” that survives when literally nothing is cacheable — at the price of a third party holding a certificate for your name and seeing your plaintext, which is a trade worth naming out loud before you put one in front of anything sensitive.

SYSTEM DESIGN CHECK

Test Your Understanding

Quick architectural scenario challenge based on this breakdown.

SCENARIO

A 3rd-party payment gateway starts timing out with 504 errors during peak traffic.

Q1.How should a downstream dependency failure be handled to protect upstream service availability?

Comments –