Contents

A CDN Is Just a Photocopier the Size of the Planet

It doesn't make your server faster, it makes the trip to your server shorter

Contents

The first time I put my own static site behind a CDN, the origin server didn’t get any faster — I hadn’t touched it — but a page load from another continent went from feeling sluggish to feeling instant, purely because of geography. A CDN, a content delivery network, doesn’t make the work of generating a response faster. It makes copies of the response and puts those copies physically closer to whoever’s asking for them, so the round trip that dominates load time on a distant request mostly disappears. It’s a photocopier network, distributed across the planet, and understanding that framing explains exactly what a CDN helps with and, just as usefully, exactly what it can’t touch.

The Problem Is Distance, Not Server Speed

Advertisement

The dominant cost in loading a page from far away usually isn’t how long the server takes to generate the response — for a static file, that’s close to zero — it’s the physical time light and electrical signals take to travel the distance involved, plus the multiple round trips a connection needs before any data even starts flowing (DNS lookup, TCP handshake, TLS handshake, then finally the actual request). Speed of light in fibre is a hard physical limit, not an engineering problem you can optimise away, and a request from Copenhagen to a server in Virginia is going to spend a meaningful chunk of its total time just crossing the Atlantic and back, regardless of how fast the server itself responds once the request arrives.

1
2
3
4
5
6
7
$ traceroute example.com
 1  10.0.0.1        1.2 ms
 2  isp-gateway      8.4 ms
 3  transit-hop-1   34.1 ms
 4  transit-hop-2   71.5 ms
 5  us-east-edge    89.2 ms
 6  example.com     91.7 ms   <- most of the time is hops 3-5, not the server

A CDN’s entire value proposition is shortening that trip by putting a copy of the content on a server much closer to the requester — often within the same city or region — so the multi-hop transatlantic journey in that traceroute simply doesn’t need to happen for content the CDN already has cached nearby. The origin server’s actual processing speed is completely unaffected either way; what changed is how far the response has to travel once it exists; and for genuinely static content, that’s most of the total load time, which is why CDNs are so effective for exactly this kind of asset and so much less effective for things that aren’t static at all.

How the Network Actually Decides Where to Send You

A CDN operates points of presence — edge servers — in many cities worldwide, and routes each request to whichever one is closest using either DNS-based routing (returning a different IP address depending on where the query originates) or Anycast (advertising the same IP address from many locations and letting internet routing itself deliver the request to the nearest advertiser). Either way, the request you send never actually reaches the true origin server unless the nearby edge doesn’t already have a cached copy of what you asked for.

1
2
3
4
$ dig cdn.example.com

;; ANSWER SECTION:
cdn.example.com.  60  IN  A  151.101.1.140   <- resolves to a nearby edge, not the true origin

When an edge server does have the content cached, it serves it directly and the origin server never even sees that request happen — this is a cache hit, and it’s the fast, cheap path a well-configured CDN aims to maximise. When the edge doesn’t have it — a cache miss, either because nobody nearby has requested this content recently or because the cached copy expired — the edge server fetches it from the origin itself, serves it to the requester, and (for cacheable content) stores a copy locally so the next nearby request becomes a hit instead. This first-request penalty is the reason CDN performance testing sometimes looks inconsistent: the very first request for a rarely-accessed asset from a new region pays the full origin round trip, while the second request moments later from someone nearby is served entirely from the edge.

What Caching Headers Are Actually Telling the Network

Advertisement

None of this caching happens by accident or guesswork — it’s governed entirely by HTTP caching headers the origin server sends, and getting these wrong is the single most common reason a CDN “isn’t helping” the way someone expected. Cache-Control: public, max-age=86400 tells every CDN edge and browser in the chain “this response can be cached by anyone, for 24 hours, then treat it as stale and re-check with the origin.” Get the max-age too short, and the CDN ends up hitting the origin almost every time anyway, defeating most of the point. Get it too long on content that actually changes, and users see stale content for hours or days after an update, with no error and no obvious explanation beyond “why hasn’t the site updated.”

1
Cache-Control: public, max-age=86400, stale-while-revalidate=3600

That stale-while-revalidate directive is worth knowing specifically because it solves a real, common annoyance: it lets the edge serve a slightly stale cached copy immediately while it quietly re-fetches a fresh one from the origin in the background, rather than making the unlucky user whose request triggers the refresh wait for that full origin round trip themselves. It’s a genuinely elegant piece of the caching contract, and support for it varies across CDN providers, which is worth checking before assuming it’s available.

Where a CDN Stops Helping Entirely

Personalised, dynamic content — anything that differs per logged-in user, a shopping cart, a live dashboard — generally can’t be cached at all in the traditional sense, because caching assumes the same response is correct for every requester, which is precisely untrue for per-user content. Putting such content behind a CDN without careful configuration risks something worse than “no speedup” — it risks one user’s private response being cached and accidentally served to a different user entirely, a class of real, documented incidents at major platforms that traces back to exactly this mistake: caching a response that varied by user without properly including that variation (via the Vary header or by simply marking the response uncacheable) in the cache key. A CDN is an accelerant for content that’s genuinely the same for everyone; used carelessly on content that isn’t, it can leak data between users instead of speeding anything up.

For genuinely dynamic, personalised, or write-heavy traffic, a CDN’s real contribution shrinks to a narrower but still useful role: terminating TLS closer to the user, absorbing traffic spikes and basic denial-of-service load before it ever reaches your origin, and (for API traffic) potentially caching specific safe, shared endpoints while passing personalised requests straight through. It’s still worth having in front of a homelab-facing service for the DDoS absorption and TLS termination benefits alone, even when the actual content behind it can’t be cached meaningfully at all.

Purging Is the Part Nobody Designs Properly Until It Bites

Caching content is the easy half of running a CDN well; invalidating it correctly on demand is the harder half, and it’s the part most teams under-design until a bad deploy makes it urgent. Time-based expiry (max-age) handles routine staleness fine on its own, but it can’t handle “I need this specific stale response gone from every edge server on the planet right now,” which is exactly what you need after publishing a correction, fixing a security-sensitive file, or shipping a broken asset that needs to disappear immediately rather than wait out its cache lifetime. Most CDN providers offer an explicit purge API for exactly this case, either by URL, by cache tag, or (the blunt, expensive option) by purging everything at once.

1
2
3
4
$ curl -X POST "https://api.cdn-provider.example.com/v1/zones/abc123/purge_cache" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://example.com/broken-asset.js"]}'

Purging by specific URL is fast and cheap; purging an entire zone is slow, sometimes rate-limited, and briefly increases origin load as every edge simultaneously loses its cache and has to refetch from the source at once — worth knowing before reaching for the blunt option reflexively during an incident, since a full purge under load can turn a minor content bug into a genuine origin traffic spike as every edge server worldwide asks for a fresh copy within the same few seconds. Tagging cacheable responses with identifiers that map to logical content (a specific article, a specific product) rather than relying purely on URL-based purging is what lets larger sites invalidate precisely what changed without either of these costs, though it requires deliberate design up front rather than being something you can bolt on cheaply after the fact.

Running the Same Idea Yourself, at Smaller Scale

The core mechanism behind a commercial CDN — cache a response near where it’s requested, serve from cache when possible, fall back to the origin on a miss — isn’t exclusive to global providers, and a reverse proxy with caching enabled in front of a homelab service does a scaled-down version of exactly the same job for anyone hitting it from the same local network or region. Nginx or Caddy configured with proxy_cache will hold a copy of responses from a slower backend and serve repeat requests directly, which is genuinely useful in front of anything that regenerates the same content repeatedly and expensively — a dashboard that recomputes the same report on every request, a media server thumbnailing the same images repeatedly for different clients.

1
2
3
4
5
6
7
8
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m max_size=1g;

location / {
    proxy_cache mycache;
    proxy_cache_valid 200 10m;
    proxy_pass http://backend:8080;
    add_header X-Cache-Status $upstream_cache_status;
}

That X-Cache-Status header is the same diagnostic idea as the commercial CDN’s cache-status header above, scaled down to a single box — HIT means the proxy served it without bothering the backend, MISS means it went to the backend and cached the result for next time. It’s a smaller version of the same photocopier idea, minus the global geography, and it’s often the more appropriate first step for a homelab service that doesn’t yet have enough distributed traffic to justify a real CDN but does have a backend slow enough that repeated identical requests are worth avoiding.

Troubleshooting: When the CDN Doesn’t Seem to Be Helping

Check response headers first — most CDNs add a header indicating cache status directly (CF-Cache-Status, X-Cache: HIT or MISS, or similar depending on provider), and a string of consistent misses on content you expected to be cached almost always traces back to caching headers from the origin being too aggressive about marking things private or too short on max-age, rather than anything wrong with the CDN configuration itself. If a page updates on the origin but visitors still see the old version, that’s expected behaviour under a long max-age, not a bug, and the fix is either purging the cache explicitly through the CDN’s API after a deploy or setting a shorter max-age with stale-while-revalidate for content that changes on a predictable schedule. If personalised content is showing up shared between users — the serious failure mode — audit the Vary header and cache-key configuration immediately, because that’s precisely the class of mistake that turns a performance tool into a data leak.

Is It Worth Setting Up?

For anything serving static assets — images, CSS, JS, a static site like this one — decisively yes, and the effort is genuinely small relative to the win, mostly configuration rather than code. For anything that’s mostly dynamic and personalised, the honest expectation should be narrower: TLS termination and DDoS absorption rather than a dramatic speed win, and it’s still worth having for those reasons even without the caching benefit doing much work. Either way, the mental model that actually explains what’s happening is geography, not raw server horsepower — a CDN is a network of very well-placed photocopiers, not a faster printing press. For a related look at what happens to the request itself once it’s travelling that shortened distance, what bandwidth and latency actually mean, and why your Wi-Fi lies covers the other half of the physical picture, and caching is just lying on purpose is the natural companion piece on the trust trade-off every cache — CDN included — is quietly making on your behalf.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.