Book Two · Water
The Edge Kingdom
The realm
A CDN — Content Delivery Network — is a large group of servers spread across the world, which keep copies of your content and serve it to whoever is nearest.
Each of those locations is called a PoP (point of presence) or, more commonly, an edge server — “edge” because it is at the outer edge of the network, close to users, rather than at the centre where your own servers are.
Your own server is called the origin — the place the content originally comes from, and the single source of truth behind all the copies.
Why geography is latency
From Lesson 1: a request across a continent takes around 150ms, and that is a physical limit. Light does not go faster because you optimised your code. Physics does not accept pull requests. 🙂
If your origin is in Virginia and your user is in Singapore, every single request pays that crossing. You cannot make the distance shorter — but you can stop making the trip. Put a copy in Singapore and the user's request travels a few miles instead of eight thousand.
And distance is only half of it. A request crossing the planet passes through dozens of routers, any of which can be congested — rather like a long-haul flight, where the hours in the air are the part you can predict, and the connections, the customs queue and the delay on the tarmac are the part you cannot.
That is all a CDN is. It does not make anything faster. It makes things closer.
How a request flows
User in Singapore
↓
Nearest edge server (Singapore)
├─ HIT → serve the stored copy ~5ms
└─ MISS → fetch from origin (Virginia) ~200ms
store the copy at the Singapore edge
serve it
↓
next Singapore user gets a HIT ~5ms
The first Singapore visitor pays full price. Everyone after them gets the local copy.
The edge is loading missing data for you, on its own, without your application being involved. That behaviour has a name — read-through caching — and you will meet it formally in Lesson 8. A CDN is a read-through cache you did not have to write.
Cache keys at the edge
Lesson 2's rule applies here with full force: the key must contain every input that changes the answer. But at the edge, you are not the one building the key — the CDN is. Your job is to configure what goes into it.
By default the key is roughly the URL. You can add parts to it or take parts away — and both of Lesson 2's mistakes are waiting for you here.
Put too much in the key and identical pages get stored separately, so nothing is ever reused.
Put too little in and different answers collide under one key, so people get somebody else's page.
The edge has a characteristic version of each.
Query parameters: too much in the key
A query parameter is the part of a URL after
?, like ?page=2 or
?utm_source=newsletter.
Some of them genuinely change the response.
/search?q=headphones and
/search?q=laptop are different pages, and the CDN must
treat them as different keys — otherwise everyone searching for
anything gets the first person's results.
So most CDNs include the whole query string in the key by default. That is the safe choice. It is also the one that quietly destroys your hit ratio.
Because if the CDN includes all of them, then these are four different keys:
/product/42 /product/42?utm_source=newsletter /product/42?utm_source=twitter /product/42?fbclid=IwAR2x9...
Four identical pages, stored four times, each starting cold. Marketing links and social-media click identifiers generate near-infinite unique URLs, so your hit ratio collapses toward zero for exactly the traffic you spent money to attract.
Key normalisation is the fix. You tell the CDN which query parameters actually change the response, and it strips the rest before building the key.
On the storefront, page and q change what
comes back. utm_source and fbclid change
nothing at all — they exist for your analytics, not for your server.
Strip them, and the four keys collapse back into one:
/product/42?utm_source=newsletter ┐ /product/42?utm_source=twitter ├─→ /product/42 /product/42?fbclid=IwAR2x9... ┘
Most CDNs let you write this either way round: name the parameters that matter and ignore everything else, or name the ones to strip. Prefer the first. A list of what matters still works when marketing invents a new tracking parameter next week. A list of what to strip does not — and it fails silently, by splitting your cache again.
The Vary header: when the URL is not enough
Query parameters are at least visible in the URL. But some responses differ based on things that are not in the URL at all: the language the browser asked for, whether it accepts compressed content, whether the user is signed in.
The URL is identical. The right answer is not. The cache has no way to know that by itself.
Vary is how you tell it. The header
declares that a response depends on something in the
request, not just the address.
Vary: Accept-Language
This says: the answer differs depending on the language the browser asked for — so store a separate copy per language. Without it, the first visitor's language would be served to everyone. That is the too-little-in-the-key failure, and it is a real bug that ships regularly.
But Vary is dangerous in the other direction too:
Vary: Cookie
This says store a separate copy for every distinct cookie value. Since almost every visitor has a unique session cookie, almost every visitor gets their own private copy that is never reused. You have turned a global cache into a very expensive way of caching nothing.
Vary: Cookie is a well-known way to accidentally
disable a CDN entirely.
Changing what the edge holds
You have pushed copies out across the world. Now you need to change one of them.
Take the product photograph. It is cached at the edge for a day, in hundreds of locations, and you have just been handed a corrected image. How do you replace thousands of copies you do not control?
Three options, and the differences matter.
| Method | How it works | Speed | Cost |
|---|---|---|---|
| TTL expiry | Wait for the copy to expire on its own | Slow — up to the full TTL | Free, automatic |
| Purge | Tell every edge to delete a specific key now | Seconds to minutes | Rate-limited by most CDNs |
| Versioned URL | Publish at a new URL; nothing to delete | Instant | Requires changing every reference |
TTL is the default and covers most cases. Pick a lifetime you can live with being wrong for.
Purge is the emergency tool — a wrong image, a legal takedown. It is not instant and not unlimited; CDNs restrict how often you can purge, so a design that purges on every write will hit a wall.
Versioning is the strongest option, and the same
trick as Book One's fingerprinted filenames. Publish
four-realms-atlas-v2.jpg and update the reference. Nothing to
invalidate, because nothing was ever changed.
When versioning is not available
Versioning works for the image because only your own HTML points at it. You change the file and the reference to it together, in one deploy.
It does not work for /product/42. That address is
bookmarked by users and indexed by search engines, so you cannot
rename it — purge and TTL are your only tools there.
Origin shield
Here is a problem that only appears once you are big.
You have 200 edge locations. A popular item's TTL expires. All 200 edges now have a miss at roughly the same moment, and all 200 go to your origin for the same file.
Your origin, which was comfortably serving a trickle of traffic, gets 200 simultaneous requests for one thing.
An origin shield is one designated edge server that sits between all the others and your origin. Edges do not go to origin — they go to the shield. The shield gets one miss, makes one origin request, and answers the other 199 from its copy.
Your origin sees one request instead of 200.
Hold on to this shape: many simultaneous misses on one key, all hitting the thing behind the cache. It is the exact structure of the cache stampede, the first attack in Lesson 12, and the origin shield is one form of the defense.
Reverse-proxy caching
A reverse proxy is a server that sits in front of your application and answers requests on its behalf. nginx and Varnish are the common ones.
It is the same idea as a CDN, with one difference: it runs on your own hardware, usually in your own datacentre, rather than being distributed worldwide.
- A CDN reduces latency by being geographically close to users.
- A reverse proxy reduces load by absorbing repeat requests before they reach your application.
Same mechanics — cache keys, TTLs, purging, Vary.
Different reason for existing. Many systems run both: a CDN
worldwide, and a reverse proxy in front of the origin behind it.
At the Storefront
| Part | Edge-cacheable? | Cache key must include |
|---|---|---|
| Asset bundle | Yes, aggressively | URL only (already fingerprinted) |
| Product images | Yes | URL, image size variant |
| Product details | Yes | URL, language |
| Price | Yes, short TTL | URL, currency, customer tier |
| Stock count | No — changes too fast | — |
| Reviews | Yes | URL, language, page number |
| Cart badge | Never | Personal — do not share the HTTP response between users |
Note what happened to price: the currency and customer tier that had to be in the key in Lesson 2 have to be in the edge key too. The same rule, at every layer.
“The CDN caches my site.”
It does not cache your site. It caches individual responses — the same point as Lesson 3, one layer further out — and only the ones your headers permit, under keys you configured.
Nobody chooses to put a session cookie in the cache key, or to let a tracking parameter split one popular page into fifty thousand cold ones. It happens because a default was never examined. The CDN did exactly what it was told. The instruction was simply never read.
The Latency Lord does not look at the content the edge is holding. He looks at how the edge decides that two requests are the same request.
He notices a header being included in the key that should not be. He notices another that should be included and is not.
He does not need to break into the Edge Kingdom. He only needs to be mistaken for someone else.