CDNs

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: A network of servers spread across the world that keep copies of your site’s files near each visitor, so pages load in milliseconds instead of waiting for a round trip to one origin server.


In plain English

Imagine your website’s “real” server (the origin) lives in Sydney. Without any help, a visitor in Berlin types your URL and their browser has to send a request all the way to Sydney, wait for it to travel back, and only then render the page. Light is fast, but it isn’t magic — each round trip across the Pacific and through Europe is ~300ms. Multiply that by the images, scripts, and stylesheets on a page, and you can easily lose a full second of perceived speed.

A CDN (Content Delivery Network) is a global fleet of servers — often hundreds of locations, called edge nodes or PoPs (Points of Presence) — that all hold copies of your static files: images, CSS, JavaScript bundles, fonts, downloadable assets, and increasingly even full HTML pages. The CDN sits in front of your origin like a global cache layer.

The first time someone in Berlin asks for logo.png, the nearest CDN edge (probably in Frankfurt) doesn’t have it, so it fetches the file from Sydney, sends it to the user, AND keeps a copy. The thousandth request from Europe never touches Sydney — it’s served directly from Frankfurt in 15ms.

That’s the whole idea. CDNs trade a tiny amount of complexity for dramatic speed gains, lower origin load, and built-in resilience.


Why it matters

Three concrete payoffs:

  1. Speed. A page that takes 2.4 seconds to load globally without a CDN typically drops to 600–900ms with one. That’s the difference between feeling “fast” and feeling “broken” to users. Google’s research is consistent: each additional second of load time meaningfully hurts conversions, retention, and search ranking.

  2. Origin protection. Your origin server doesn’t have to handle every request. The CDN absorbs 80–99% of traffic on a typical content-heavy site. This also means your origin survives traffic spikes — a viral moment that would crush a bare server gets absorbed at the edge.

  3. DDoS and abuse defense. CDNs sit in front of you and can detect, throttle, or block malicious traffic patterns before they ever reach your origin. Cloudflare in particular has built its business on this.

A secondary benefit: many CDNs run code at the edge now (Vercel Edge Functions, Cloudflare Workers, Fastly Compute@Edge). The CDN has evolved from “static cache” to “place where dynamic logic also runs close to users.”


How a CDN actually works

The flow when a user requests a file:

1. User's browser → resolves DNS for `cdn.example.com`
                    ↓
2. DNS returns the IP of the nearest CDN edge (geographic routing)
                    ↓
3. Browser → connects to that edge node
                    ↓
4. Edge node checks its cache:
   - HIT  →  return the cached file. Done.
   - MISS →  fetch from origin, store a copy, return to user.
                    ↓
5. Subsequent users in the region get a HIT (until the file expires)

Two questions decide whether you get a hit or a miss:

  • Is the file in cache at this edge? Each edge has its own cache. A file cached in Frankfurt isn’t necessarily cached in Tokyo until someone asks for it there.
  • Has the cached copy expired? Each cached object has a TTL set by HTTP cache headers from the origin (or override rules at the CDN). When the TTL expires, the next request triggers a re-fetch from origin.

What gets cached — and what doesn’t

The classic distinction:

CacheableNot cacheable by default
Static images (.png, .jpg, .svg, .webp, .avif)Logged-in user pages with personal data
CSS and JavaScript bundlesAPI responses that change per user
FontsAnything with Cache-Control: no-store
Video segmentsForm POST responses
Public HTML pagesAnything that sets cookies (some CDNs auto-skip these)
PDFs, downloadsServer-Sent Events / websockets

Modern hosts and frameworks blur this line. Vercel’s Incremental Static Regeneration (ISR) caches HTML at the edge but allows it to be revalidated on a schedule. Cloudflare’s Tiered Caching even caches some dynamic responses if you tell it to. The line keeps moving.


Cache headers — the language of CDNs

The origin tells the CDN how to cache things via HTTP response headers. The two you absolutely need to know:

Cache-Control: public, max-age=31536000, immutable
  • public — caches (browser + CDN) can store this
  • max-age=31536000 — for 1 year (in seconds)
  • immutable — never check back, the content will never change at this URL

That’s what you want for content-hashed assets like app.a3f8b2.js — they have a unique URL per version, so they can be cached “forever.”

Cache-Control: public, s-maxage=60, stale-while-revalidate=86400
  • s-maxage=60 — CDN caches for 60 seconds (the s- prefix means “shared cache only, browsers ignore”)
  • stale-while-revalidate=86400 — even after 60s, serve stale content for up to 1 day while fetching fresh content in the background

That second pattern is the workhorse for modern dynamic content: a page is “fresh” for a minute, can be served slightly stale for a day while it re-builds, and only fully refreshes if a user hits a truly stale window. It’s the magic behind ISR.


A concrete example: an image on stmarkbible.com

When you load the home page, an image like /images/cross.png goes through:

  1. Browser checks local cache. If you’ve visited recently, the file is on disk. No network request. Instant render.

  2. Browser checks the CDN. If local cache missed, the browser sends a request to Vercel’s edge. Vercel’s CDN (running on Cloudflare-like infrastructure) is at hundreds of edge locations. The nearest one to you (Sydney for an Australian user) checks its cache.

  3. Edge HIT. Vercel’s edge in Sydney has the file. Returns it in ~15ms with cache-control: public, max-age=31536000, immutable and x-vercel-cache: HIT headers so debugging is easy.

  4. Edge MISS (first request after a deploy). Vercel’s edge requests the file from origin storage. Stores a copy. Returns it. The next request is a HIT.

The user doesn’t see any of this. They just see “the page loaded fast.”

For Next.js’s <Image> component on Vercel, an extra layer kicks in: the image is also resized, reformatted (AVIF/WebP), and re-cached per device size. The same source PNG might be served as 4 different optimized variants depending on the requester.


Cache invalidation — the hard problem

The famous Phil Karlton quote: “There are only two hard things in Computer Science: cache invalidation and naming things.”

When you update a file, how do you tell the world’s CDN edges to throw away the old version?

Three approaches:

1. Wait for TTL expiration

Simplest. Set a sensible TTL; the old version expires naturally. Painful for content you’ve just fixed an urgent bug in.

2. Purge

Tell the CDN to drop a specific URL (or pattern). Most CDNs support a purge API or dashboard button. Cloudflare lets you purge by URL, by hostname, or “Purge Everything” (nuclear option). The purge usually propagates worldwide in seconds.

3. Content-hashed URLs

The most elegant. Instead of updating /app.js, ship /app-a3f8b2.js. Different filename = different cache entry = no invalidation needed. Modern bundlers (Webpack, Vite, Turbopack) do this automatically. The HTML page that references the asset DOES need to be re-cached, but HTML is much smaller and much rarer in volume than the JS/CSS/images it references.

The 2026 standard: immutable fingerprinted assets for everything you build, short TTLs + purge for HTML pages. Combined, this gives you “instant updates” without abandoning aggressive caching.


The CDN landscape in 2026

ProviderStrengths
CloudflareHuge network (300+ cities), free tier with no bandwidth limits, also a security + Workers platform
FastlyBest programmability via VCL, popular with big media + ecommerce, used by GitHub, Stripe
AWS CloudFrontTight integration with rest of AWS, complex but powerful
AkamaiThe original CDN. Enterprise-grade, expensive, used by banks + telcos
Vercel Edge NetworkBuilt-in for Vercel projects, no separate configuration
Netlify EdgeSame idea, for Netlify projects
Bunny.netCheap, simple, popular with indie hackers

For typical webapp work, you don’t pick a CDN — your hosting provider includes one. Vercel projects use Vercel’s edge; Cloudflare Pages projects use Cloudflare’s. You only think about explicit CDN choice when:

  • You’re using a self-hosted origin (a VPS, a custom server)
  • You want to put Cloudflare in front of another host for DDoS protection or free SSL
  • You have very specific edge logic needs (Fastly’s VCL is wildly capable)

CDN as more than a cache

Modern CDNs offer a buffet of related features:

  • Image optimization. Resize, reformat, compress on the fly. Vercel’s Image, Cloudflare Images, Bunny Stream.
  • Edge functions. Run JavaScript / WASM at the edge. Cloudflare Workers, Vercel Edge Functions.
  • Bot management. Distinguish humans, friendly bots (Googlebot), and abusive bots.
  • DDoS mitigation. Absorb floods at the edge before they reach your origin.
  • Web Application Firewall (WAF). Block SQL injection, XSS, exploit attempts at the edge.
  • Geographic blocking / steering. “Block traffic from countries we don’t serve” or “redirect AU users to the AU site.”
  • A/B testing & redirects at the edge — sub-millisecond decision before reaching origin.
  • Real User Monitoring (RUM). Many CDNs offer Web Vitals tracking baked in.

The CDN is no longer “just a cache” — it’s the programmable layer your app sits behind.


Common gotchas

  • Cache hit ratio is the metric to watch. A cache that never hits is just adding latency. CDN dashboards usually show “hit ratio.” Anything below 80% on static assets is a configuration smell.

  • Vary headers can shred your hit ratio. Vary: User-Agent tells the CDN to store a separate cached copy per user-agent string — and there are millions of unique user-agents in the wild. Use Vary very sparingly.

  • Cookies often disqualify caching by default. Many CDNs auto-skip the cache for any response that sets a cookie. Configure carefully if you want to cache pages that include cookies.

  • Cache-Control from origin matters more than you think. A misconfigured origin can override your CDN’s defaults. If the origin sends Cache-Control: no-store, the CDN respects it. Audit your headers.

  • HTML caching is where everyone trips. Caching /products/123 is wonderful — until you ship a critical fix and the world keeps seeing the old page for 24 hours. Use ISR + revalidation, or short HTML TTLs with proactive purging.

  • The “thundering herd” problem. When a popular cached object expires, every edge fetches it from origin at once. CDNs solve this with request coalescing (deduplicating concurrent origin requests). Cheap CDNs may not. Know if yours does.

  • You can’t cache personalized pages naively. A page that shows “Welcome, George” is different per user. Either render personalization client-side (and cache the shell) or use edge-side personalization with VARY on a user-id header.

  • CORS and CDNs interact subtly. If your CDN strips or modifies Origin headers, cross-origin requests can break. Especially relevant for fonts.

  • Purging takes time, but usually less than you fear. Most modern CDNs propagate purges globally in 1–30 seconds. Older or self-rolled CDNs can take minutes.

  • Bandwidth costs can dominate hosting costs. Generally cheaper than serving from origin, but at scale, CDN bandwidth fees can be significant. Cloudflare’s “no bandwidth fee” model is unusual; most others charge by GB out.

  • Geo-targeting from a CDN’s perspective ≠ from a browser’s. A user in a VPN exit node is “located” where the VPN exits. Don’t make critical decisions on CDN-supplied geo data without considering this.

  • HTTP/2 multiplexing changes the calculus. With HTTP/1, browsers limited 6 connections per origin, so domain sharding (assets1.example.com, assets2.example.com) helped parallelism. With HTTP/2, sharding HURTS because it splits the multiplexed connection. Use a single hostname.

  • CDN debugging headers are your friend. x-vercel-cache, cf-cache-status, x-cache etc. tell you HIT/MISS/STALE per response. Check them via DevTools Network panel when investigating slow loads.

  • Static asset hashing breaks if you don’t deploy atomically. When you push a new version, the new HTML references new hashed assets — but for a brief window, the CDN may serve old HTML referencing new (404) assets, or new HTML referencing old (still-cached) assets. Hosts like Vercel handle this atomically; rolling your own takes care.

  • Origins still need to be reachable. A CDN with 0% hit ratio is worse than no CDN. If origin goes down, the cache buys you some time, but expired objects can’t be served. Use stale-if-error or a circuit-breaker to extend stale-serving during origin outages.

  • The CDN sees your traffic. Putting Cloudflare in front of your site means Cloudflare can read every request (it terminates TLS). For most projects this is fine. For privacy-critical apps (legal, healthcare), evaluate carefully — some CDNs offer end-to-end TLS pass-through but that disables features like caching and WAF.


See also


Sources