Courses › System Design › Question Breakdowns
Design a CDN
Anycast vs. GeoDNS, hashing inside a PoP, tiered caching and origin shielding, cache keys and global purge.
Design a content delivery network like Cloudflare, Akamai or CloudFront: hundreds of edge locations worldwide cache customers' content close to users, absorb traffic spikes and attacks, and fetch from the customer's origin only on a miss. It's a question about routing users to a nearby healthy edge, multi-tier caching, cache keys and invalidation, protecting the origin, and operating a global fleet. (For *using* a CDN in other designs, see Content Delivery Networks (CDN); this breakdown is about *building* one.)
Requirements
Functional
- Customers configure a site (domain → origin server) and cache rules (TTLs, what to cache)
- Users requesting the site's content are served from a nearby edge, which fetches from the origin on a miss
- Customers can purge content (by URL, tag or everything) and see it take effect globally
- Edges terminate TLS for customers' domains
Out of scope: edge compute (Workers/Lambda@Edge — mention it), video packaging, DNS-as-a-product, bot management.
Non-functional
- Low latency: most users within ~20–50ms of an edge; cache hits served in a few ms
- Massive scale: ~50M requests/second globally, tens of Tbps
- High hit ratio (95%+ for static content) to protect origins
- Fast purge: global invalidation in seconds
- Highly available: a failed server or entire location is routed around automatically; survives DDoS
[!TIP] Two problems: getting to the edge, and the edge itself Split the answer into request routing (DNS or anycast gets the user to a good location) and the edge's cache hierarchy (how a request is served once it arrives). Each has its own failure modes and trade-offs.
Capacity estimates
Core Entities
- Site / zone — customer, hostnames, origin(s), TLS certificates, cache rules, version
- Cached object — cache key, body, headers, expiry, tags, stored at a location
- Purge request — site, URLs/tags/prefix, created at, status per location
- Edge location / server — region, capacity, health
API
PUT /v1/sites/{siteId}/config { "origin": "origin.shop.com", "rules": [{ "path": "/static/*", "ttl": 86400 }] }
POST /v1/sites/{siteId}/purge { "tags": ["product-123"] } → 202 { "purgeId": "p_5" }
GET /v1/purges/{purgeId} → { "status": "complete", "locations": 312 }
# end users just do: GET https://shop.com/static/app.9f2c.jsHigh-Level Design
user = client "Users" @0,1
edge = cache "Edge PoP\n(L1 cache, TLS)" @1,1
tier = cache "Regional Tier /\nShield Cache" @2,1
origin = service "Customer Origin" @3,1
ctl = service "Control Plane\n(config, purge)" @2,0
cfg = store "Config Store" @3,0
logs = queue "Logs & Metrics" @2,2
user -> edge : anycast / GeoDNS
edge -> tier : miss
tier -> origin : miss
ctl -> cfg
ctl --> edge : push config + purges
edge --> logs- Routing: the user's request reaches a nearby edge PoP via anycast (one IP announced everywhere) or GeoDNS.
- Edge: terminates TLS, applies the site's rules, and checks its cache. A hit is served immediately.
- Miss: the edge asks a regional tier / shield cache before the origin, so the origin sees one request per object per region rather than one per PoP.
- Control plane: customers' config changes and purges are pushed to every location within seconds.
- Logs/metrics stream back for analytics and billing.
Deep Dives
1. Getting users to the right edge
Cloudflare uses anycast broadly; many CDNs combine both. Inside a PoP, a load balancer (often ECMP + consistent hashing) spreads requests across servers.
2. Caching inside a location
A PoP has many servers. If every server cached everything independently, each object would be fetched once per server. Instead, consistent-hash the cache key across servers in the PoP, so each object lives on one (or two) servers; the receiving server proxies to the owner. RAM holds the hottest objects, SSD the rest; eviction is LRU-like with admission filters (don't let one-hit wonders evict popular content).
3. Tiered caching and origin shielding
Without tiers, a popular new object misses at all 300 PoPs → 300 origin requests. With a shield (one designated upper-tier location per origin region), PoPs fetch from the shield, and the shield fetches from origin once. Combine with request coalescing: concurrent misses for the same key wait on a single upstream fetch.
[!WARNING] The cache stampede on expiry When a hot object expires everywhere at once, a flood of revalidations hits the origin. Use stale-while-revalidate (serve the stale copy while one request refreshes it), jittered TTLs, and coalescing.
4. Cache keys and correctness
The cache key is normally scheme + host + path + (selected) query string, plus any headers in Vary (e.g. Accept-Encoding). Mistakes here are the classic CDN bugs:
- Including irrelevant query params (
?utm_source=) destroys the hit ratio — normalize them. - Caching responses that depend on cookies/auth leaks one user's page to another — never cache
Set-Cookieresponses or private content by default.
5. Purge in seconds
The control plane writes the purge and fans it out to every PoP over a persistent push channel (pub/sub tree); each server marks matching keys invalid. Tag-based purge (Cache-Tag: product-123 on responses) lets customers invalidate all pages showing a product with one call — maintain a tag → keys index per server. Purge status is complete when all PoPs acknowledge. For versioned assets (app.9f2c.js), prefer immutable URLs and long TTLs over purging.
6. TLS at the edge
Edges terminate TLS for millions of customer domains: certificates are distributed to PoPs (private keys protected, or kept off-edge with keyless signing), selected per request via SNI, with session resumption and HTTP/2 or HTTP/3 (QUIC) to cut handshake latency.
7. Failures and attacks
- Health checks remove bad servers from the PoP's hash ring; a failed PoP withdraws its anycast route and traffic shifts to neighbors.
- DDoS: anycast spreads attack traffic across all PoPs; edges drop malformed and rate-limited traffic before it reaches caches or the origin. The origin should only accept traffic from the CDN.
What's expected at each level
- Mid-level: edge caches near users, origin fetch on a miss, TTLs and purge.
- Senior: GeoDNS vs. anycast, consistent hashing within a PoP, tiered caching with origin shielding and request coalescing, cache-key normalization.
- Staff: global purge propagation and tag indexes, stale-while-revalidate and stampede control, TLS at scale, DDoS absorption, and PoP failure/traffic engineering.