Courses › System Design › Core Concepts

Content Delivery Networks (CDN)

Edge caching, Cache-Control, versioned URLs vs purging, and what belongs on a CDN.

A Content Delivery Network is a fleet of cache servers spread across hundreds of cities. Users fetch content from the nearest one — the edge — instead of crossing an ocean to reach your servers. Since a transcontinental round trip costs 100–150 ms no matter how fast your code is, a CDN is the main way to make a global product feel fast, and it takes huge amounts of load off your origin.

How it works

  • The user's request is routed to a nearby point of presence (PoP), usually through DNS or anycast.
  • If the edge has the content cached (a hit), it responds immediately.
  • On a miss, the edge fetches from your origin (a web server or a blob storage bucket), caches the response, and serves it.
  • Later requests from that region are hits until the content expires.
u1 = client "User (Tokyo)" @0,0
u2 = client "User (Paris)" @0,2
e1 = cache "Edge PoP\nTokyo" @1,0
e2 = cache "Edge PoP\nParis" @1,2
shield = cache "Origin Shield" @2,1
origin = blob "Origin\n(S3 / web servers)" @3,1
u1 -> e1
u2 -> e2
e1 --> shield : miss
e2 --> shield : miss
shield --> origin : miss

Many CDNs add a middle tier, the origin shield: edges miss into one regional cache instead of all hitting your origin at once.

What to put on a CDN

  • Static assets: JavaScript, CSS, fonts, images. Always.
  • Media: images and video segments. Video streaming is basically a CDN problem (Design YouTube / Netflix).
  • Public, cacheable API responses: a product page or a trending list that's the same for everyone, cached for a few seconds.
  • Not personalized or private responses, unless you're careful with cache keys. Caching a page with another user's data in it is a classic incident.

Controlling caching

The origin tells the CDN (and browsers) what to do through HTTP headers:

  • Cache-Control: public, max-age=31536000, immutable for versioned assets that never change.
  • Cache-Control: s-maxage=60 to cache at the CDN for 60 seconds but not in browsers.
  • stale-while-revalidate to serve a slightly old copy while fetching a fresh one in the background.
  • The cache key is usually the URL; add headers or cookies to it only when the response truly varies by them.

Invalidation

Two approaches, and the first is much better:

  • Versioned URLs: put a content hash in the filename (app.3f9a1c.js). A new deploy means a new URL, so nothing needs purging and old files can be cached forever.
  • Purging: ask the CDN to drop a path. It works, but it takes seconds to minutes to reach every PoP, and you'll be tempted to do it in a hurry during an incident.

For content that changes often, prefer a short TTL over frequent purges.

Push vs pull

  • Pull CDN (the default): the edge fetches from the origin on demand. Zero setup per file; the first user in each region pays the miss.
  • Push CDN: you upload content to the CDN ahead of time. Useful for big, predictable releases such as a new episode that millions will watch at once.

More than caching

Modern CDNs (Cloudflare, CloudFront, Fastly, Akamai) also terminate TLS close to the user, absorb DDoS attacks, run a web application firewall, and can run small functions at the edge for redirects, auth checks or A/B routing.

[!INTERVIEW] Quantify the win "Images are served from CloudFront with S3 as the origin. With a 95% hit rate, the origin sees 1/20th of the traffic, and users get images from a nearby edge in ~20 ms instead of ~150 ms across the Atlantic."