What is it? #
A CDN is a network of servers spread around the world that hold copies of your content and serve it from wherever the user is.
Two things improve. Distance: light takes real time to travel, and a request crossing continents costs hundreds of milliseconds before any work is done. Load: content served from the edge never reaches your servers at all.
It is most effective for static files — images, scripts, stylesheets, fonts, video — but modern CDNs also cache API responses and whole pages when you tell them it is safe.
The hard part is not enabling it. It is deciding what may be cached, for how long, and how to update it when content changes.
Think of it like this #
A chain of local warehouses instead of one central depot. Popular items are stocked nearby, so most orders ship from a few kilometres away rather than across the country.
The central depot still holds everything and restocks the warehouses. Nobody waits for a long-distance delivery unless the item is unusual.
Simple example #
Your server is in Mumbai and a user in Berlin loads a page with thirty assets. Without a CDN, every asset makes the round trip. With one, they come from a European edge in a few milliseconds, and your server sees only the initial page request.
Code #
Without a CDN With a CDN
Berlin user Berlin user
│ ~250ms round trip │ ~15ms
▼ ▼
Mumbai server (all 30 assets) Frankfurt edge (cached assets)
│ only on a cache miss
▼
Mumbai origin server
Cache headers that control CDN behaviour
Cache-Control: public, max-age=31536000, immutable
versioned assets — app.a1b2c3.js — cache for a year, never revalidate
Cache-Control: public, max-age=300, stale-while-revalidate=86400
a page that may change — serve cached for 5 minutes, then refresh in
the background while still serving the old copy
Cache-Control: private, no-store
user-specific or sensitive — never cache anywhere
Vary: Accept-Encoding, Accept-Language
cache separate copies per encoding and language
# Is this response coming from the edge?
curl -sI https://example.com/static/app.a1b2c3.js | grep -Ei "cache|age|x-cache"
# X-Cache: HIT served from the edge
# X-Cache: MISS fetched from your origin and now cached
# Age: 412 seconds since it was cached
Versioned filenames beat invalidation
BAD /static/app.js with a long cache time
→ changing it requires a purge, and old copies linger
GOOD /static/app.a1b2c3.js with a one-year cache time
→ a new build produces a new filename, so there is nothing to purge
How it works #
A CDN edge receives the request. If it holds a fresh copy, it returns it immediately — that is a cache hit, and your server is never contacted.
On a miss, the edge fetches from your origin server, stores the response according to its cache headers, and serves it. The next user in that region gets a hit.
The cache key is usually the URL plus whatever the Vary header lists. Two URLs differing by a query parameter are separate entries, which is why tracking parameters can quietly destroy your hit rate.
immutable tells the browser and edge not even to revalidate. It is safe only when the filename changes whenever the content does, which is exactly what build-time hashing provides.
stale-while-revalidate is the most useful directive for semi-dynamic content. Users always get an instant response, and the refresh happens in the background.
Purging is the escape hatch, and it is slower and less reliable than versioning. The versioned filename approach in the last block avoids the problem entirely: new content has a new URL, so there is no stale copy to chase.
Anything user-specific must be marked private, no-store. Caching a logged-in page at a shared edge and serving it to another user is a real and serious incident.
Real-world use #
Every significant site uses a CDN. Cloudflare, Fastly, CloudFront and others offer it, and static hosting platforms include it by default.
Beyond speed, CDNs absorb traffic spikes and denial-of-service attacks, because the edge takes the load rather than your origin. That protective role is often the main reason to use one.
Edge computing extends this further, running small pieces of logic at the edge — redirects, authentication checks, personalisation — without a round trip to origin.
The most common practical problems are stale content after a deploy, caching a personalised page by accident, and low hit rates caused by query parameters or overly specific Vary headers. Checking X-Cache and Age headers is the quickest way to diagnose all three.
Common mistakes #
- Caching pages that contain user-specific data at a shared edge.
- Using unversioned filenames with long cache times, then fighting stale copies.
- Letting tracking query parameters fragment the cache key and destroy the hit rate.
- Setting
Varyon headers that differ per user, making every response unique. - Assuming a CDN fixes a slow origin. Cache misses still hit your server.
Practice #
Take a page you control and list every asset. Decide the cache policy for each: versioned and immutable, short with revalidation, or never cached. Then use curl -sI on a CDN-served asset and identify whether it was a hit, and how old the cached copy is.