Requirements & scope
Problem Statement & Requirements
Design a globally distributed Content Delivery Network that caches and serves static and dynamic content from edge locations closest to users — similar to CloudFront, Cloudflare, Akamai, or Fastly.
Functional Requirements
- Cache and serve static assets — images, videos, CSS, JS, fonts, HTML pages
- Support dynamic content acceleration — API responses, personalized pages via edge compute
- Content purge / invalidation — origin can invalidate cached content globally within seconds
- Custom domain support — customers bring their own domains with TLS (SNI-based)
- TLS termination at the edge — HTTPS everywhere, automatic certificate management
- Origin shielding — collapse multiple edge requests into a single origin fetch
- Geo-restriction — block or allow content by country/region
- Real-time analytics — hits, misses, bandwidth, latency, error rates per PoP
- Edge compute — run lightweight functions (like Cloudflare Workers) at the edge
Non-Functional Requirements
- Ultra-low latency — serve cached content in < 10 ms (p50), < 50 ms (p99) globally
- High availability — 99.999% uptime (< 5 min downtime/year)
- Massive throughput — handle 100M+ requests/second globally
- Global reach — 200+ Points of Presence (PoPs) across 50+ countries
- Cache hit ratio — target > 95% for static content
- Instant purge — invalidate content globally within < 5 seconds
- DDoS protection — absorb multi-Tbps volumetric attacks at the edge
Out of Scope
- Full WAF (Web Application Firewall) rule engine details
- Detailed DNS infrastructure design (covered as a component, not the focus)
- Video live-streaming specific protocols (HLS/DASH segmentation)
Scale estimations
Scale Estimations
Traffic
| Metric | Value |
|---|---|
| Total requests / second (global) | 100M RPS |
| Peak requests / second | 300M RPS (3x during events) |
| Number of PoPs | 250 |
| Avg requests per PoP | 400K RPS (uneven — top 20 PoPs handle 60%) |
| Cache hit ratio (static) | 95% |
| Cache hit ratio (dynamic) | 60-70% (with edge compute) |
| Origin requests / second | 5M RPS (5% miss rate on 100M) |
Bandwidth
| Metric | Value |
|---|---|
| Average response size | 50 KB (mix of images, JS, API) |
| Total egress bandwidth | 100M × 50 KB = 5 TB/s = 40 Tbps |
| Per-PoP average bandwidth | 40 Tbps / 250 = 160 Gbps per PoP |
| Top PoPs bandwidth | 500 Gbps - 1 Tbps |
| Monthly data transfer | 5 TB/s × 86,400 × 30 = ~13 EB/month |
Storage (Per PoP)
| Metric | Value |
|---|---|
| Hot content (frequently accessed) | ~2-10 TB per PoP (SSD) |
| Warm content (less frequent) | ~50-200 TB per PoP (HDD) |
| Total unique content across all origins | Hundreds of PB |
| Content served from cache (by volume) | ~95% (power-law distribution) |
Hardware Estimate (Per PoP — Medium Tier)
| Component | Spec |
|---|---|
| Edge servers | 20-100 servers per PoP |
| CPU per server | 32-64 cores (TLS termination + edge compute) |
| RAM per server | 256-512 GB (in-memory hot cache) |
| SSD per server | 4-8 TB NVMe (warm cache) |
| HDD per server | 20-50 TB (cold cache tier, large PoPs only) |
| Network per server | 25-100 Gbps NIC |
| PoP uplink | 400 Gbps - 2 Tbps (peering + transit) |
Layered architecture
High-Level Architecture
High-Level ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
Component Breakdown
Component BreakdownExcalidraw diagram · editable shapes · reveal step by stepExplore
API & contracts
API Design
Customer Configuration API
POST /api/v1/distributions
Content-Type: application/json
Request:
{
"name": "my-website-cdn",
"origins": [
{
"domain": "origin.example.com",
"protocol": "https",
"port": 443,
"path": "/assets",
"weight": 100, // for origin failover / load balancing
"timeout_ms": 30000,
"retry_count": 2
}
],
"domains": ["cdn.example.com", "assets.example.com"],
"cache_behaviors": [
{
"path_pattern": "/images/*",
"ttl_seconds": 86400, // 24 hours
"compress": true,
"allowed_methods": ["GET", "HEAD"],
"cache_key_includes": ["query_string", "accept_encoding"]
},
{
"path_pattern": "/api/*",
"ttl_seconds": 0, // pass-through to origin
"allowed_methods": ["GET", "POST", "PUT", "DELETE"],
"forward_headers": ["Authorization", "Accept"]
}
],
"tls": {
"certificate": "auto", // auto-provision via Let's Encrypt
"min_protocol_version": "TLSv1.2",
"http2": true
},
"geo_restriction": {
"type": "whitelist",
"countries": ["US", "CA", "GB", "DE", "JP"]
}
}
Response (201 Created):
{
"distribution_id": "d-abc123",
"status": "deploying",
"cdn_domain": "d-abc123.cdn.net",
"custom_domains": ["cdn.example.com"],
"created_at": "2026-04-03T10:00:00Z",
"estimated_deploy_time_seconds": 120
}Purge / Invalidation API
POST /api/v1/distributions/{distribution_id}/invalidations
Content-Type: application/json
Request:
{
"paths": [
"/images/hero.jpg", // exact path
"/css/*", // wildcard
"/*" // purge everything (use sparingly)
]
}
Response (202 Accepted):
{
"invalidation_id": "inv-xyz789",
"status": "in_progress",
"paths": ["/images/hero.jpg", "/css/*"],
"created_at": "2026-04-03T10:05:00Z",
"estimated_completion_seconds": 5
}Analytics API
GET /api/v1/distributions/{distribution_id}/analytics
?start=2026-04-03T00:00:00Z
&end=2026-04-03T23:59:59Z
&granularity=1h
&metrics=requests,bandwidth,cache_hit_ratio,p99_latency
Response (200 OK):
{
"distribution_id": "d-abc123",
"data_points": [
{
"timestamp": "2026-04-03T00:00:00Z",
"requests": 45000000,
"bandwidth_gb": 2250,
"cache_hit_ratio": 0.967,
"p99_latency_ms": 28,
"error_rate_4xx": 0.012,
"error_rate_5xx": 0.0003
},
...
]
}Data model
Data Model
Distribution Configuration
Distribution ConfigurationExcalidraw diagram · editable shapes · reveal step by stepExplore
Cache Entry (Per Edge Server — In-Memory/On-Disk)
Cache Entry (Per Edge Server — In-Memory/On-Disk)Excalidraw diagram · editable shapes · reveal step by stepExplore
Access Log Record (Streamed to Analytics)
Access Log Record (Streamed to Analytics)Excalidraw diagram · editable shapes · reveal step by stepExplore
Core design decisions
Core Design Decisions
Decision 1: Request Routing — How Users Reach the Nearest PoP
This is the most critical decision. Every request must land on the optimal PoP.
Option A: DNS-Based GeoDNS
Option A: DNS-Based GeoDNSExcalidraw diagram · editable shapes · reveal step by stepExplore
| Pros | Cons |
|---|---|
| Simple, well-understood | Routing based on DNS resolver location, not user |
| Works with any client | Can't react to real-time congestion |
| No special client support needed | DNS caching delays failover |
Option B: Anycast IP Routing
Option B: Anycast IP RoutingExcalidraw diagram · editable shapes · reveal step by stepExplore
| Pros | Cons |
|---|---|
| Routes based on actual network topology | BGP convergence can take 30-90s on failure |
| Automatic failover via BGP withdrawal | TCP connections break on route changes |
| No DNS dependency for routing | Less granular control than DNS |
| Inherently absorbs DDoS (spreads across PoPs) | Can't control per-customer routing easily |
Option C: Hybrid (DNS + Anycast) — Recommended
Option C: Hybrid (DNS + Anycast) — RecommendedExcalidraw diagram · editable shapes · reveal step by stepExplore
Decision 2: Cache Hierarchy — Multi-Tier Caching
Decision 2: Cache Hierarchy — Multi-Tier CachingExcalidraw diagram · editable shapes · reveal step by stepExplore
Decision 3: Cache Invalidation Strategy
Decision 3: Cache Invalidation StrategyExcalidraw diagram · editable shapes · reveal step by stepExplore
Request flows
Detailed Flow Diagrams
Cache Hit Flow (Happy Path — 95% of Requests)
Cache Hit Flow (Happy Path — 95% of Requests)Excalidraw diagram · editable shapes · reveal step by stepExplore
Cache Miss Flow (with Origin Shield)
Cache Miss Flow (with Origin Shield)Excalidraw diagram · editable shapes · reveal step by stepExplore
Purge / Invalidation Flow
Purge / Invalidation FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
Performance & caching
Workshop note · added for the website’s common reading format
Start with the dominant access pattern of Content Delivery Network. Customer origin is one place to inspect capacity and tail latency. Measure before introducing a cache: define the cache key, invalidation policy, stale-data budget, and cold-start behavior.
Batch independent work where the latency budget allows it. Bound queues and concurrency, and verify that an optimization does not move the bottleneck to a dependency.
Advanced design
TLS at the Edge
TLS Termination Architecture
TLS Termination ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
OCSP Stapling
Traditional OCSP:
Client → CDN → serve content
Client → CA's OCSP responder → "is cert still valid?" (SLOW, privacy leak)
OCSP Stapling:
CDN periodically fetches OCSP response from CA
CDN staples (attaches) OCSP response to TLS handshake
Client gets cert + validity proof in one step → faster, more privateEdge Compute
Architecture
ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
Edge Compute Deployment Flow
Edge Compute Deployment FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
DDoS Mitigation at the Edge
DDoS Mitigation at the EdgeExcalidraw diagram · editable shapes · reveal step by stepExplore
Edge cases
Handling Edge Cases
Thundering Herd (Cache Stampede)
Problem: Popular object expires → 10,000 concurrent requests all miss cache
→ all 10,000 hit origin simultaneously → origin overload
Solution: Request Coalescing (aka Request Collapsing)
Request 1 (cache miss) → lock: I'll fetch from origin
Request 2 (cache miss) → wait, someone is already fetching
Request 3 (cache miss) → wait, someone is already fetching
...
Request 10,000 (cache miss) → wait
Origin receives: 1 request (not 10,000)
Response returns → populate cache → serve all 10,000 waiters
Implementation: per-cache-key mutex with a wait queue
Timeout: if origin is slow (> 5s), serve stale content if availableStale Content During Origin Failure
Problem: Origin is down → cache misses can't be filled → users get errors
Solution: Stale-If-Error
Cache-Control: max-age=60, stale-if-error=86400
Normal: serve fresh content (age < 60s)
Origin down: serve stale content (age < 86400s) + add Warning header
Completely expired: return 502/504 with custom error page
Edge behavior:
1. Cache miss → try origin → timeout/5xx
2. Check stale cache → if stale copy exists, serve it
3. Add header: Warning: 110 "Response is Stale"
4. Log origin failure → alert monitoringHot Object / Viral Content
Problem: One object (e.g., breaking news image) gets 1M RPS at a single PoP
Solution: Multi-layer approach
1. RAM cache (L1) absorbs most reads — kernel-level serving (io_uring / sendfile)
2. If single server saturates → PoP-level LB distributes across all servers
3. All servers in PoP independently cache the object (no shared cache needed)
4. Pre-warm: detect virality (rapid access count increase) → proactively
push to all PoPs before they miss
Key insight: CDN edge servers are embarrassingly parallel for reads.
Unlike databases, every server can independently cache + serve the same object.Cache Poisoning
Problem: Attacker sends crafted request → poisoned response gets cached
→ all subsequent users get the poisoned response
Attack: GET /page HTTP/1.1
Host: cdn.example.com
X-Forwarded-Host: evil.com ← if origin reflects this, cached for all users
Mitigations:
1. Strict cache key construction — only include documented Vary headers
2. Never cache responses with Set-Cookie headers
3. Strip untrusted headers before forwarding to origin
4. Validate Cache-Control directives from origin (ignore private/no-store only for intended objects)
5. Cache key includes Host header — prevents cross-domain poisoning
6. WAF rules to detect header injection attemptsPartial Content / Range Requests (Video Seeking)
Client: GET /video.mp4
Range: bytes=1000000-2000000
Edge behavior:
Option A: Cache full object, serve range from cache
+ Simple — one cached copy serves any range
- Wastes bandwidth on first fetch if user only watches 30s of a 2-hour video
Option B: Cache range slices (e.g., 1 MB chunks)
+ Only fetch what's needed
- Complex cache management, many small cache entries
- Reassembly overhead
Recommended: Hybrid
→ Small objects (< 10 MB): cache full object
→ Large objects (> 10 MB): cache in fixed-size slices (2 MB)
→ Use internal Range requests to origin for slices
→ Serve client's Range from assembled slicesTradeoffs
Tradeoffs & Design Decisions Summary
| Decision | Option A | Option B | Chosen | Why |
|---|---|---|---|---|
| Request Routing | DNS-based GeoDNS | Anycast BGP | Hybrid (Anycast + DNS) | Anycast for coarse routing + DDoS absorption; DNS for fine-grained control |
| Cache Hierarchy | Single-tier edge only | Multi-tier (edge + shield) | Multi-tier | Origin shield reduces origin load 30x; justified for any serious CDN |
| Cache Eviction | Pure LRU | LFU (frequency-based) | Hybrid LRU+LFU (ARC/W-TinyLFU) | LRU evicts popular items on scan; LFU is slow to adapt; hybrid is best |
| Cache Invalidation | TTL-only | Active purge | TTL + SWR + Surrogate Keys | TTL for baseline; SWR for zero-latency; tags for surgical invalidation |
| TLS Termination | At origin | At edge | Edge | Eliminates 100-200 ms per new connection; enables HTTP/2 multiplexing |
| Edge Compute | Containers (Lambda@Edge) | V8 Isolates (Workers) | V8 Isolates | Sub-ms cold start vs 100ms+; thousands per process; better for request-level compute |
| Cache Storage | In-memory only | Disk + memory tiered | Tiered (RAM → SSD → HDD) | Memory alone can't hold enough; SSD gives 10x capacity at ~5 ms; HDD for cold tail |
| Origin Protocol | HTTP/1.1 persistent | HTTP/2 multiplexed | HTTP/2 | Single connection, multiplexed streams; reduces origin connection overhead |
| Content Compression | gzip | Brotli | Both (Brotli preferred) | Brotli: 20-30% smaller than gzip for text; pre-compress at cache time |
| PoP Failure | DNS failover (30-60s) | BGP withdrawal (30-90s) | BGP + health-check triggered DNS | BGP is automatic; DNS gives faster override; combined gives < 30s failover |
Reliability & fault tolerance
Reliability & Fault Tolerance
Single Points of Failure & Mitigations
Single Points of Failure & MitigationsExcalidraw diagram · editable shapes · reveal step by stepExplore
Graceful Degradation Hierarchy
Level 1 (single server failure):
→ LB routes around it. Zero user impact. Auto-heals.
Level 2 (partial PoP degradation):
→ Remaining servers absorb traffic. Increased latency at that PoP.
→ Alert ops team.
Level 3 (full PoP failure):
→ BGP withdrawal in ~30s. Users rerouted to next-closest PoP.
→ Latency increases by ~10-50 ms. No data loss.
Level 4 (origin shield failure):
→ Edges bypass shield, fetch directly from origin.
→ Origin sees higher load but still functional.
Level 5 (customer origin failure):
→ Serve stale cached content (stale-if-error).
→ Custom error pages for truly uncached content.
→ Alert customer via webhook.
Level 6 (control plane failure):
→ Edges continue serving with last-known config.
→ No config updates or purges until control plane recovers.
→ Edge-to-edge gossip for critical updates (if implemented).Production architecture
Full System Architecture (Production-Grade)
Full System Architecture (Production-Grade)Excalidraw diagram · editable shapes · reveal step by stepExplore
Further exploration
Workshop note · added for the website’s common reading format
Rebuild Content Delivery Network from memory, then change one assumption: ten times more traffic, a new region, or a stricter consistency requirement. Which component must change first—and which does not?
Compare Edge PoP with the same boundary in a related design. Write down one alternative you rejected, what it would simplify, and when you would choose it instead. Follow the source link at the end of this article to explore the original document.
Interview playbook
Interview Tips
Start with the two-sentence pitch — "A CDN is a globally distributed cache. It serves content from the edge closest to users, reducing latency from ~200 ms (origin) to ~5 ms (edge cache hit)."
Draw the DNS → Edge → Shield → Origin hierarchy first — This is the backbone. Explain each tier's purpose and cache hit contribution.
Discuss Anycast deeply — Most candidates only mention DNS routing. Explain how Anycast works (same IP, BGP announces from every PoP), why it's elegant (automatic failover, DDoS dilution), and its limitations (TCP connection breaks on route change).
Cache key construction is subtle — Don't handwave. Explain how the cache key is built from path + query + Vary headers + distribution ID. Mention cache poisoning risks.
Explain the thundering herd problem and request coalescing — This shows you've thought about real production issues, not just the happy path.
Stale-while-revalidate is a killer feature — Explain the tradeoff: users always get fast responses, content is at most one cycle stale, and origins never block the user.
TLS at the edge is non-negotiable — Quantify: terminating TLS at edge vs origin saves 1-2 RTTs × distance. For a user 200 ms from origin but 5 ms from edge, that's 390 ms saved on TLS 1.2.
Mention edge compute — This differentiates you. Explain V8 isolates (sub-ms cold start), use cases (A/B testing, auth, geolocation), and why they're superior to containers for request-level compute.
Discuss cost model — CDN economics matter. Bandwidth is the main cost (peering vs transit). Explain why CDNs negotiate private peering with ISPs and why edge cache capacity planning uses power-law distributions.
End with DDoS — "A CDN is inherently a DDoS mitigation layer. With 250 PoPs each handling Tbps, the network absorbs attacks by distributing them. The origin never sees the attack traffic."