CDN - caching at the edge
Your origin server is fast. But if the user is 10,000 km away, physics dominates. A CDN moves the cache to 300+ cities worldwide - the request never reaches your origin.
11 min read
This video presents visual lesson highlights with instrumental background music. The complete lesson is available as text below.
Day 2 taught you caching: store the result, serve the cached copy. But that cache lived inside your server. A user in Singapore still has to send a request to your server in Virginia, wait for it to cross the Pacific, hit your cache, and come back. That’s 200ms of network latency before your cache even fires.
A Content Delivery Network (CDN) moves the cache to the edge - to data centers in 300+ cities worldwide. The user’s request hits a nearby CDN edge node (POP), and if the content is cached there, the response comes from nearby, not from your origin. Your origin server never sees the request.
The problem: physics is slow
Light travels through fiber at about 200,000 km per second - roughly two-thirds the speed of light in a vacuum. That sounds fast, but:
- London to New York: 5,567 km → ~28ms one way, ~56ms round trip
- Tokyo to Virginia: 10,800 km → ~54ms one way, ~108ms round trip
- Sydney to London: 17,000 km → ~85ms one way, ~170ms round trip
And that’s the theoretical minimum. Real-world paths add routing overhead, peering hops, and congestion. A request from Tokyo to a server in Virginia typically takes 150-250ms round trip, before the server does any work.
You can optimize your database to return in 2ms. You can cache a query in 0.1ms. But if the user is 10,000 km away, the network latency dominates. You can’t cheat physics - but you can move the server closer.
The CDN: 300 POPs, one origin
A CDN is a distributed network of Points of Presence (POPs) - edge servers in cities worldwide. Each POP caches content from your origin server. When a user requests a URL, the CDN’s DNS routes them to the nearest POP.
The anatomy of a CDN request
- User in Tokyo types
example.com/image.png - DNS resolves to the CDN’s anycast IP - routes to the Tokyo POP (not your Virginia origin)
- Tokyo POP checks its cache:
- Cache HIT: Return the file from Tokyo (5ms). Origin never contacted.
- Cache MISS: Forward request to origin in Virginia (200ms), cache the response, return to user (215ms total)
TTL: how long the edge holds your content
The Cache-Control header tells the CDN how long to cache:
Cache-Control: public, max-age=3600
public- any cache (CDN, browser) can store itmax-age=3600- cache for 3600 seconds (1 hour)
After TTL expires, the POP evicts the cached copy. The next request is a cache miss - the POP fetches a fresh copy from the origin and re-caches it.
s-maxage: different TTL for CDN vs browser
Cache-Control: public, max-age=60, s-maxage=3600
max-age=60- browser caches for 60 secondss-maxage=3600- CDN (shared cache) caches for 3600 seconds
This lets you have a short browser TTL (users see updates within 1 minute) but a long CDN TTL (the edge serves cached copies for an hour, shielding your origin).
The benchmark: origin vs CDN edge
I simulated three scenarios using Node.js with setTimeout to model realistic network latency:
- Origin only: Request travels to a remote origin (200ms RTT) + origin processing (10ms).
- CDN cache hit: Request hits a nearby edge POP (5ms RTT) + cache lookup (1ms).
- CDN cache miss: Request hits edge POP (5ms RTT) + edge fetches from origin (200ms RTT) + origin processing (10ms).
| Scenario | Median | P95 | P99 |
|---|---|---|---|
| Origin (no CDN) | 428.4ms | 439.6ms | 453.6ms |
| CDN cache HIT | 51.2ms | 87.8ms | 106.1ms |
| CDN cache MISS | 251.0ms | 289.3ms | 308.0ms |
Speedup: 8.4× faster on cache hit. But a cache miss (251ms) is actually faster than direct origin (428ms) - the CDN POP has a better network path to the origin than the user does. Even on a miss, the CDN helps.
Cache hit ratio: the metric that matters
The CDN’s value depends entirely on cache hit ratio - what percentage of requests are served from the edge cache:
| Hit Ratio | Avg Latency | Status |
|---|---|---|
| 95% | 61.2ms | ✅ |
| 90% | 71.1ms | ✅ |
| 80% | 91.1ms | ✅ |
| 70% | 111.1ms | ❌ |
| 50% | 151.1ms | ❌ |
At 90% hit ratio, average latency is 71ms - fast enough for any application. At 50%, it’s 151ms - barely better than no CDN at all. The CDN only pays off when the cache hit ratio is high.
What to cache vs what NOT to cache
| Content Type | Cache at CDN? | TTL | Example |
|---|---|---|---|
| Static assets (CSS, JS, images, fonts) | ✅ Yes | Long (1yr) | app.a8f3b2.css |
| Static HTML pages | ✅ Yes | Medium (1hr) | Blog posts |
| Computed pages (SSR) | ✅ Yes (with care) | Short (60s) | Product pages |
| API responses (public data) | ✅ Yes | Short (60s) | Weather, stock prices |
| API responses (user-specific) | ❌ No | - | /api/user/profile |
| Real-time data | ❌ No | - | WebSockets, chat |
| Authentication tokens | ❌ No | - | JWT, session cookies |
Rule of thumb: Cache public, immutable, or slowly-changing content. Never cache user-specific or real-time data.
Cache invalidation: the two strategies
Strategy 1: TTL expiration (passive)
Let the cache expire naturally. Set a TTL, and after it expires, the next request fetches a fresh copy. Simple, but users might see stale content for up to TTL duration.
Strategy 2: Cache-busting (active)
Use content-hashed filenames so each version has a unique URL:
<link rel="stylesheet" href="/css/app.a8f3b2.css">
<link rel="stylesheet" href="/css/app.c7d9e1.css">
The new file has a new URL - the CDN fetches it fresh. The old URL stays cached until TTL expires, but no one requests it anymore. This is why build tools like Vite, Webpack, and Astro generate content-hashed filenames.
Strategy 3: Purge API (nuclear option)
Most CDN providers offer a purge API:
# Cloudflare
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone}/purge_cache" \
-H "Authorization: Bearer {token}" \
-d '{"files":["https://example.com/css/app.css"]}'
# AWS CloudFront
aws cloudfront create-invalidation \
--distribution-id {id} \
--paths "/*"
Purging is immediate but expensive - it wipes the cache, so the next request is a guaranteed miss. Use sparingly.
Stale-while-revalidate: the best of both worlds
Cache-Control: public, max-age=60, stale-while-revalidate=600
- For the first 60 seconds: serve from cache (fast)
- From 60-660 seconds: serve stale content AND fetch fresh in the background
- After 660 seconds: must fetch fresh
The user always gets a fast response. The cache updates in the background. This is the gold standard for content that updates frequently but can tolerate slight staleness.
CDN vs Redis: layered caching
| Layer | Location | Latency | What it caches |
|---|---|---|---|
| Browser cache | User’s device | 0ms | Static assets, HTML |
| CDN edge cache | POP in user’s city | ~5ms | Static assets, pages, public API |
| Redis (app cache) | Your data center | ~1ms | Database query results, computed data |
| Database cache | Your data center | ~2ms | Hot data in shared buffers |
Day 2 taught you Redis - caching at the application layer. A CDN adds a layer above Redis: caching at the network edge, before the request even reaches your data center.
The full request path
- Browser cache → hit? return (0ms)
- CDN edge cache → hit? return (5ms)
- API gateway (Day 16) → auth, rate limit, route
- Redis cache (Day 2) → hit? return (1ms + network)
- Database → execute query, return (2-50ms)
Each layer catches different types of requests. The CDN catches static assets and public pages. Redis catches database queries and computed data. Together, they can reduce origin load by 99%+.
The connection: the caching curriculum
- Day 2: Caching (Redis - cache at the application layer)
- Day 7: Load balancing (distribute traffic across servers)
- Day 14: Message queues (decouple async work)
- Day 16: API gateway (the front door - routing, auth, rate limiting)
- Day 21: CDN (cache at the edge - before the request reaches your infrastructure)
The progression: cache queries → balance traffic → decouple work → centralize the entry point → move the cache to the edge. A CDN is the outermost cache - the one closest to the user.
1. Your CDN cache hit ratio is 60%. Average latency is 151ms. Your boss says “just increase the TTL from 1 hour to 24 hours.” What’s the strongest argument against this?
2. A user in Sydney reports that their API requests are sometimes 51ms and sometimes 251ms. The CDN POP in Sydney is 5ms away and the origin in Virginia is 210ms away. What’s happening?
3. You deploy a new CSS file but users still see the old styles. The CDN has the old file cached with TTL 1 year. What’s the best fix?
Your turn - the teach step Close this lesson. Write the “Explain like I’m 10” and the “60-second LinkedIn version” from memory. Focus on: what a CDN is, why cache hit ratio matters, how TTL and cache-busting work, and how CDN complements Redis from Day 2. Post it, and paste the link.