13

Content Delivery Networks (CDN)

A first-principles tour of edge PoPs, anycast routing, the cache hierarchy, and the headers that control it all.

Why a CDN exists

A CDN (Content Delivery Network) is a globally distributed fleet of caching servers that sit between your users and your origin — the server that holds the authoritative copy of your content. The core problem it solves is physics: data cannot travel faster than light, so a user in Sydney fetching from a server in Virginia pays an unavoidable ~150ms each way, and a page needing many round trips feels sluggish no matter how fast your server is. A CDN fixes this by keeping copies of your content on machines physically near the user.

The CDN buys you three things at once, and it helps to keep them distinct: lower latency (a nearby server answers in milliseconds), origin offload (the CDN absorbs the read traffic, so your origin sees a trickle instead of a flood), and resilience (the edge can keep serving cached content even if the origin is briefly down or under a traffic spike).

The whole day is about one question: how does the CDN get the right content, to the right edge, near the right user, and know when that content has gone stale?

Edge PoPs and anycast routing

An edge PoP (Point of Presence) is one of the CDN's data-center locations — a cluster of cache servers in a city close to users. A large CDN has hundreds of PoPs worldwide. The trick is making each user's request land at the nearest PoP automatically, and the mechanism is anycast.

Anycast is a routing scheme where the same IP address is announced from many locations at once. When a user connects to that IP, the internet's routing (BGP) naturally delivers the request to the topologically closest PoP announcing it. The user does nothing; the network does the geography. (Some CDNs instead use DNS-based routing, returning a different PoP's IP per user based on the resolver's location — same goal, different layer.)

The benefit beyond latency: anycast also gives you DDoS resilience for free. A flood of attack traffic is spread across every PoP announcing the IP rather than concentrated on one machine, so the CDN's aggregate capacity absorbs attacks that would flatten a single origin.

Request routing: hit, miss, and origin fetch

The heart of a CDN is what happens when a request reaches a PoP: it either has the content (hit) or it does not (miss), and a miss triggers a fetch up the hierarchy. Understanding this path is understanding the CDN.

Two terms make this precise. A cache hit means the edge already holds a fresh copy and serves it immediately without touching the origin — the fast, cheap path you want the overwhelming majority of requests to take. A cache miss means the edge must fetch from upstream first; the first user to request an uncached object pays the full origin round trip, then every user after them at that PoP gets a hit until the copy expires. The hit ratio (hits ÷ total requests) is the single most important CDN metric: a 95% hit ratio means the origin sees only 1 in 20 requests.

The cache hierarchy: edge, shield, origin

A CDN is not one layer of cache but a hierarchy, and the middle layer — the origin shield — is what protects your origin from a stampede. Without it, a miss at every PoP would send that many independent fetches to your origin.

The layers, top to bottom:

  • Edge PoPs are the many caches nearest users. On a miss, instead of going straight to origin, they ask the shield.
  • The origin shield is a single PoP (or a small tier) designated as the intermediary between all edges and the origin. When 200 edges each miss on the same new object, they all ask the shield; the shield fetches from origin once and serves the other 199 from its own cache. This collapses N origin fetches into one.
  • The origin is your authoritative server, reached only when even the shield misses.

The shield turns a potential thundering herd — many simultaneous origin fetches for one newly-popular or newly-expired object — into a single fetch. It is the CDN-scale version of request coalescing, and enabling it is often the highest-leverage change for a struggling origin.

Cache keys and TTL

For the cache to work, the edge must decide what counts as the same object and how long to keep it. Those are the cache key and the TTL, and getting them wrong is the most common CDN misconfiguration.

The cache key is the identity the CDN uses to look up a stored object — by default the full URL (host + path + query string). Two requests with the same key share a cached copy; two with different keys do not. This matters enormously: if you include a per-user tracking parameter like ?utm_source=x in the key, every user gets a unique key, nothing is ever shared, and your hit ratio collapses to zero. You configure the CDN to ignore irrelevant query params, or to include the ones that genuinely change the response, and optionally to vary on headers like Accept-Encoding (so a gzip and a non-gzip response don't clobber each other).

The TTL (Time To Live) is how long an edge treats a cached object as fresh before it must revalidate or refetch. Longer TTLs mean higher hit ratios and less origin load, but slower propagation of changes. The tension is exactly the caching trade-off: freshness versus efficiency.

Content typeSensible TTLWhy
Hashed static asset (app.9f2c1.js)1 year, immutableFilename changes when content changes
Images, fontsdays to weeksRarely change; large offload
HTML pagesseconds to minutesChange often; want fresh-ish
Personalized / auth'd responsesno-storeMust never be shared between users

Cache-Control headers in practice

The origin controls all of the above by attaching HTTP headers to its responses. The CDN reads them to decide whether, how, and how long to cache. This is the contract between your origin and the edge, so it is worth seeing real headers.

HTTP/1.1 200 OK
Content-Type: application/javascript
Cache-Control: public, max-age=31536000, immutable
ETag: "9f2c1a"

The directives you will actually use:

  • public — any cache, including the shared CDN, may store this. private — only the user's browser may (never the CDN); use for per-user data.
  • max-age=<seconds> — the browser's freshness lifetime. s-maxage=<seconds> — a separate, override lifetime for shared caches like the CDN, letting you cache long at the edge but briefly in the browser.
  • no-store — never cache this anywhere (auth'd, sensitive). no-cache — you may store it but must revalidate with the origin before each use.
  • immutable — this will never change, so don't even revalidate; the ideal for content-hashed filenames.
  • stale-while-revalidate=<seconds> — serve the stale copy instantly while fetching a fresh one in the background, so users never wait on a revalidation.

Revalidation uses ETag (a content fingerprint) or Last-Modified: the edge sends If-None-Match: "9f2c1a" and the origin replies 304 Not Modified with no body if nothing changed — cheap confirmation instead of resending the whole object.

Purge and invalidation

TTLs handle expected staleness, but sometimes you change content and cannot wait for the TTL — a wrong price, a legal takedown, a bad deploy. Purge (also called invalidation) is the operation that forces the edge to drop a cached object before its TTL expires.

Two things to know about purges. First, they take seconds to propagate across a global fleet — it is a correction, not an instant switch, so never rely on purge for time-critical correctness. Second, purging is operationally noisy at scale, which is why the preferred pattern avoids it entirely: content-hashed filenames (app.9f2c1.js). When the content changes, the filename changes, so it's simply a new, uncached URL — the old one expires on its own and no purge is needed. Reserve purge for the HTML that references those hashed assets, and for emergencies. CDNs also offer tag-based purge (invalidate every object tagged product-42 at once) for cases where one logical change touches many URLs.

Push vs pull, static vs dynamic, edge compute

Three final distinctions round out the mental model, each a design lever you will actually choose between.

Pull vs push CDN — how content gets to the edge:

Pull CDNPush CDN
How content arrivesLazily, on first miss (edge fetches from origin)You upload content to the CDN ahead of time
Best forLarge catalogs, most web contentA few large, known files (software downloads, video)
First requestSlow (a miss)Fast (already there)
EffortZero — just set headersYou manage uploads/expiry

Pull is the default for almost everything; push suits a small set of big files you know will be requested.

Static vs dynamic acceleration — static content (images, CSS, JS) caches trivially because it is identical for everyone. Dynamic content (a personalized dashboard, a search result) can't be cached as-is, but a CDN still accelerates it via connection optimization: the user's TLS handshake terminates at the nearby PoP, and the PoP holds a warm, reused connection back to origin over the CDN's optimized private network — cutting round trips even for uncacheable responses.

Edge compute — CDNs now run your code at the PoP (Cloudflare Workers, Lambda@Edge). This pushes logic — auth checks, A/B routing, request rewriting, personalization, assembling a page from cached fragments — to within milliseconds of the user, blurring the line between "CDN" and "compute platform." It lets you personalize at the edge without a full origin round trip.

Key takeaways

  • A CDN is a global fleet of caches near users that cuts latency, offloads the origin, and adds resilience — it beats the physics of round-trip distance.
  • Anycast announces one IP from every PoP so the network routes each user to the nearest one automatically, and spreads DDoS load across the whole fleet.
  • The unit of success is the hit ratio: hits serve instantly from the edge; a miss fetches upstream and the first user pays the origin round trip.
  • The origin shield collapses N per-PoP misses into a single origin fetch, turning a thundering herd into one request.
  • The cache key (default: full URL) decides what's shared — include a per-user param and the hit ratio dies. TTL trades freshness for efficiency.
  • Cache-Control headers are the origin↔edge contract: public/private, max-age vs s-maxage, no-store, immutable, stale-while-revalidate; ETag enables cheap 304 revalidation.
  • Purge is a slow-propagating correction, not an instant switch — prefer content-hashed filenames so a change is just a new URL.
  • Pull CDN is the default; push suits a few big files. Even uncacheable dynamic content is accelerated via edge TLS termination and warm origin connections, and edge compute runs your logic at the PoP.

Checklist

  • [ ] I can explain the three things a CDN buys: latency, origin offload, resilience.
  • [ ] I understand how anycast routes users to the nearest PoP and helps absorb DDoS.
  • [ ] I can trace a request through hit, miss, and origin fetch, and define hit ratio.
  • [ ] I know what an origin shield does and why it prevents an origin stampede.
  • [ ] I can configure a cache key to ignore tracking params and preserve hit ratio.
  • [ ] I can set correct Cache-Control headers for static, dynamic, and per-user responses.
  • [ ] I prefer content-hashed filenames over purge and know why purge is a slow correction.
  • [ ] I can choose pull vs push and explain how dynamic content and edge compute are accelerated.