System designCore9 min

Content delivery networks (CDNs)

Serve your files from a server a few milliseconds from each reader instead of one on another continent, and keep most of the traffic off your own servers.

1 · The problem

A far server is slow however fast it is

A news site runs its servers in Virginia. A story breaks, and readers in Sydney open it. Between them and Virginia is about 200 ms of round trip, measured between cloud regions, and nothing the site does to its servers can shorten it.

Round trips add up before a single byte of the story arrives. A browser opening a new secure connection needs:

  • one round trip for the TCP handshake;
  • one for the TLS 1.3 handshake;
  • one for the request and the first bytes of the answer.

That is three round trips, 3 × 200 = 600 ms, for the page alone, and the page then asks for images, scripts and styles.

New connection to Virginia
600 ms before the first byte
New connection to a Sydney edge
15 ms before the first byte
Edge miss, fetched from Virginia
215 ms before the first byte

Three round trips of 200 ms against three of about 5 ms. Even when the nearby server has to fetch the file, it already holds an open connection to Virginia, so it adds one round trip, not three: 15 + 200 = 215 ms.

A content delivery network (CDN) is a fleet of servers, called edge servers, spread across hundreds of cities. Readers connect to the nearest one, and it answers from its own cache. The site's own servers, the origin, are asked only for what the edge doesn't have.

2 · How it works

An edge server answers nearby readers from its cache

  1. The reader asks for the image

    DNS, or routing that sends one address to the nearest site, points the browser at a healthy edge nearby.

  2. The edge checks its cache

    On a hit it answers at once, from Sydney.

  3. On a miss it asks the origin

    Over a connection it keeps open, then stores the answer for as long as the headers allow.

  4. Everyone nearby now gets a hit

    The origin sent the file once to this city, not once per reader.

The first reader in the city pays one trip to Virginia; for the next ten minutes, everyone else is served from Sydney.

Most CDNs work this way, as pull CDNs: they fetch from the origin on demand. A push CDN gets files uploaded ahead of time instead, which suits large, predictable content such as a catalog of video files, and saves the first reader's miss.

3 · The origin's view

The hit ratio decides what still reaches your servers

The hit ratio is the share of requests an edge answers from its cache. The origin sees the rest, so small changes near the top matter a lot. For a million reads of the story's image:

Hit ratio

Requests that reach the origin

90%

100,000

99%

10,000

99.9%

1,000

Going from 99% to 99.9% looks like a rounding error and cuts the origin's share ten times. The ceiling is set by the lifetime: an edge holding the image for 10 minutes asks the origin no more than once every 10 minutes, however many readers it serves. With 300 edges, that is still up to 300 fetches per 10 minutes, one per city with readers.

Readersin 300 citiesEdge serversone per cityShield tiernear the originOriginVirginiamillions of readsmisses onlyone per lifetimeReadersin 300 citiesEdge serversone per cityShield tiernear the originOriginVirginiamillions of readsmisses onlyone per lifetime

With a middle tier, edges that miss ask the shield, not the origin. The origin sees one fetch per file per lifetime instead of one per city.

Two features push the origin's share down further:

  • A second cache tier, often called an origin shield: edges that miss ask a regional cache near the origin, and only that cache asks the origin.
  • Request collapsing: when thousands of readers ask for the same uncached file in the same moment, the edge sends one request to the origin and answers all of them from it. Without it, the first second of a viral story becomes thousands of identical origin requests, a stampede like those in Cache failure modes.

4 · The cache key

The cache key decides which requests share one copy

An edge stores each answer under a cache key, usually the host and the full URL. Two requests share a copy only if their keys match, so anything that makes keys differ quietly lowers the hit ratio:

What varies

What happens

The fix

Tracking parameters, ?utm_source=…

Every campaign link is a new key and a new miss

Leave those parameters out of the key

Vary: User-Agent

One copy per browser version, thousands of them

Vary only on what changes the answer, such as Accept-Encoding

Cookies on the request

Many CDNs skip the cache or key on them

Serve shared files from a cookie-free path or domain

Set-Cookie on the answer

Many CDNs won't store it at all

Don't set cookies on shared files

Check the defaults of the CDN you use. One large CDN, for example, doesn't cache HTML or JSON unless told to, and never stores a response that sets a cookie or says private, no-store, no-cache or max-age=0.

5 · Keeping it fresh

Headers set the lifetime; new file names make old copies harmless

The origin says how long a copy may be reused in the Cache-Control response header, a list of directives that browsers and every shared cache on the way obey. The ones that matter on a CDN:

  • max-age: how long browsers and the CDN may reuse the answer;
  • s-maxage: a separate lifetime for shared caches such as the CDN, so the edge can keep a page for a minute while browsers check every time;
  • stale-while-revalidate=30: after the lifetime ends, serve the old copy for up to 30 more seconds while fetching a new one in the background;
  • stale-if-error: keep serving the old copy if the origin is failing;
  • immutable: this URL will never change, so don't even check on reload.

File

Header

Why

app.3f9a1c.js, images, fonts

public, max-age=31536000, immutable

A new build gets a new name, so a copy can live a year

The story's HTML

public, s-maxage=60, max-age=0, stale-while-revalidate=30

The edge absorbs the crowd; an edit shows within a minute

A reader's account page

private, no-store

Never stored by a shared cache

A purge tells the CDN to drop a URL now, and on modern CDNs it is fast: one reports a median under 150 ms worldwide for purges by tag or prefix. But a purge reaches only the CDN. Browsers, and any other cache that stored the file under its max-age, keep their copies. That is why long-lived files get a new name for each version instead of being purged: the page simply points at the new URL, and the old copies are never asked for again.

6 · What goes on a CDN

Cache what is the same for everyone, never what is personal

Content

Fit

Why

Images, scripts, styles, fonts

Ideal

The same for everyone, and they change rarely

Video segments

Ideal

Huge, and the popular ones are watched by many

Public pages and API answers

Good, with short lifetimes

A minute of caching takes most of a spike

Pages with personal data

Never as a whole

Cache the shared parts and fetch the personal ones separately

The last row is the expensive mistake. On Christmas Day 2015, during an attack on the Steam store, a caching rule changed in a hurry served store pages built for one user to other users. For about 90 minutes, pages for roughly 34,000 people, some with billing addresses and purchase histories, could be seen by strangers. A shared cache cannot tell whose page it holds unless the key or the headers say so.

The same reach helps when traffic spikes for any reason. The CDN's network is far larger than the origin, so a crowd, or many kinds of attack, lands on the edges first, and the origin sees only the misses. That is also the reason to watch the hit ratio during a spike: a cache key that suddenly varies turns the CDN's capacity back into the origin's problem.

Check yourself

5 questions

1. Readers in Sydney load a site hosted in Virginia, about 200 ms away. Why does a CDN make the first page load much faster?
2. An edge cache's hit ratio for a popular image rises from 99% to 99.9%. What happens to the requests that reach the origin?
3. A new build ships a changed app.js. What is the most reliable way to make sure readers get it?
4. Marketing links add ?utm_source=… to every URL, and the CDN keys on the full URL. What happens?
5. Which page should never be stored whole by a shared cache?

Takeaways

Remember this

  • A CDN beats distance: handshakes and requests go to an edge a few milliseconds away instead of an origin hundreds away.
  • The origin sees the misses: at a 99.9% hit ratio, a tenth of what it sees at 99%.
  • A shield tier and request collapsing cut the origin to about one fetch per file per lifetime.
  • Keep cache keys tight: drop tracking parameters, vary only on what changes the answer, and keep cookies off shared files.
  • Give long-lived files a new name per version with a year's lifetime; a purge reaches the CDN but not the browsers.