Network & delivery

7 min readUpdated

What is a CDN (Content Delivery Network)?

Quick answer

A content delivery network (CDN) is a group of servers distributed across many locations that store copies of your content and serve it to visitors from whichever location is closest to them. Instead of every request travelling to your single origin server, most are answered nearby. This cuts latency, reduces the load your own infrastructure has to handle, and — because a cached response never touches your origin — absorbs a large share of attack traffic as a side effect.

01How a CDN works

When a visitor requests a file, DNS or anycast routing directs them to a nearby CDN location rather than to your server. If that location already holds a fresh copy, it is served immediately: a cache hit. If not, the CDN fetches it from your origin once, keeps a copy and serves later requests from its own storage: a cache miss followed by hits.

How long a copy is kept is controlled by the cache headers your origin sends, chiefly Cache-Control, defined in RFC 9111. A fingerprinted asset can be served from the edge for a year; an HTML page might be revalidated every minute; a logged-in dashboard should never be stored by a shared cache at all.

RequestEdgecache lookupHITServed hereorigin idleMISSYour originfetch oncethencached
A cache hit is answered at the edge and never reaches your origin — which is also why caching absorbs attack traffic.
Typical cache headers
# Fingerprinted asset: the URL changes whenever the file does
Cache-Control: public, max-age=31536000, immutable

# Public HTML: fresh for a minute, then served stale while it refreshes
Cache-Control: public, max-age=60, stale-while-revalidate=600

# Anything personalised: never store
Cache-Control: private, no-store

02What to cache, and for how long

The right lifetime depends on how a URL changes, not on its file type. Two extensions do much of the work: immutable (RFC 8246) tells caches that a response will not change during its freshness lifetime, and stale-while-revalidate (RFC 5861) lets a cache keep serving a just-expired copy while it fetches a new one in the background. Status codes have defaults of their own: a 301 or 404 sent without cache headers may still be stored, as which HTTP status codes can be cached explains.

ContentExampleCache-ControlWhy
Fingerprinted assets/assets/app.3f9c2b.jspublic, max-age=31536000, immutableA new build produces a new URL, so the old one never has to change
Unversioned images and fonts/logo.pngpublic, max-age=86400Changes are rare and a day of staleness is acceptable
Public HTMLAn article or product pagepublic, max-age=60, stale-while-revalidate=600Short freshness keeps it current; stale serving hides origin latency
Public API dataA price list in JSONpublic, max-age=0, s-maxage=30Browsers revalidate every time while the CDN absorbs the repeats
Personalised pages/account, /cartprivate, no-storeA shared cache must never hand one user's page to another

03What you actually get

  • Lower latency: distance is physics, and a response from 30 km away beats one from 3,000 km
  • Less origin load: cached responses never reach your server, so it can be smaller
  • Traffic spikes absorbed: a viral page is served from the edge instead of melting your origin
  • Lower bandwidth bills: you pay for the cache fill, not for every visitor, which also lowers the peaks a 95th percentile bill is calculated from
  • Attack resilience: flood traffic hitting cached URLs is answered without your origin noticing

04The part that goes wrong: caching the wrong thing

The most damaging CDN misconfiguration is caching a personalised response and serving it to someone else. If a page containing one user's name, basket or session is stored at the edge, the next visitor may receive it. That is a data breach caused by a caching rule.

The safeguards are straightforward but must be deliberate. The HTTP caching standard, RFC 9111, already forbids a shared cache from reusing a response to a request that carried an Authorization header unless the response explicitly allows it, and common proxy caches skip responses that set cookies: nginx's documentation states that a response with Set-Cookie 'will not be cached'. Treat anything behind a login as uncacheable unless you have thought carefully about it, and mark it private or no-store so the outcome never rests on defaults.

05How to check whether a response came from cache

Most CDNs report what they did, though not always in the same header. The Age header (RFC 9111) gives the seconds since the response was fetched or revalidated at the origin, so a non-zero Age usually means a cached copy. Cache-Status (RFC 9211) is the standard form, with values such as hit or fwd=miss; many providers still use their own X-Cache header.

Request the same URL twice and compare. A second response that is still a miss points to a header that prevents caching, a cookie, or a cache key that differs on every request. The HTTP headers checker lists every response header and shows which CDN answered, without installing anything.

Checking cache headers from the command line
curl -sI https://example.com/assets/app.css | grep -iE '^(cache-control|age|cache-status|x-cache|vary|set-cookie):'

# A cached asset typically answers with something like:
cache-control: public, max-age=31536000, immutable
age: 5321
cache-status: ExampleCDN; hit

06Tiered caching and cache stampedes

A naive CDN has a problem when something popular expires: every location that gets a request at that moment goes to the origin at once, and a single expiry becomes a burst of simultaneous requests. This is a cache stampede.

Two mechanisms prevent it. Request coalescing means concurrent misses for the same URL at one location produce a single origin fetch, with the others waiting for its result. Tiered caching adds an intermediate layer, so edge locations fetch from a regional cache rather than all reaching your server independently.

If you run your own cache in front of an application, the same ideas take a few lines of nginx. The configuration below follows your origin's Cache-Control headers, lets one request per missing item through to the origin while others wait (for up to proxy_cache_lock_timeout, five seconds by default), and keeps serving the stale copy while it refreshes or when the origin fails.

A self-hosted cache with coalescing and stale serving (nginx)
# http {} context
proxy_cache_path /var/cache/nginx/app levels=1:2 keys_zone=app:10m
                 max_size=1g inactive=60m use_temp_path=off;

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_cache app;
        proxy_cache_lock on;
        proxy_cache_use_stale updating error timeout http_502 http_503 http_504;
        proxy_cache_background_update on;
        add_header X-Cache-Status $upstream_cache_status always;
    }
}

07Cache keys and cache-busting

A cache stores each response under a key. RFC 9111 defines the key as the request method and target URI, and nginx's default key combines the scheme, the upstream host and the full request URI, query string included. The Vary header adds request headers to the key: Vary: Accept-Encoding is harmless, while Vary: User-Agent or Vary: Cookie can split one page into thousands of copies and wreck your hit ratio.

The same mechanism is how floods get past a cache. Appending a random query string makes every request a new key, so every request is a miss that reaches your origin. It is a standard technique in HTTP floods, and the defences are the usual ones: ignore or normalise query parameters a route does not use, keep uncacheable routes behind rate limits and WAF rules, and make sure the origin can refuse work cheaply.

08Why CDNs and security belong together

A CDN and a web application firewall occupy the same position: both are a reverse proxy sitting between your visitors and your origin. Running them as one system rather than two means a request is terminated once, inspected, and served or forwarded in a single hop, instead of paying the cost of two proxies in sequence.

It also makes caching part of your defence. During an attack, requests for URLs already in cache are answered at the edge and never reach your origin. Attack traffic aimed at cacheable content is, from the origin's point of view, absorbed for free.

09Troubleshooting common CDN problems

SymptomLikely causeFix
Visitors see the old version after a deployA long max-age on an unversioned URLFingerprint file names, or purge the changed URLs
The hit ratio stays lowSet-Cookie on static responses, Vary on User-Agent or Cookie, unique query stringsStop setting cookies on assets, trim Vary, normalise query strings
A user sees someone else's account pageA personalised response was stored by the shared cachePurge at once, mark the route private, no-store, and audit every cache rule
The CDN returns 502The origin refused or reset the connection, or the TLS handshake to it failedWork through 502 Bad Gateway causes; Cloudflare reports the same failures as 52x error codes
The CDN returns 504The origin accepted the request but answered too slowlyFind the slow endpoint; see 504 Gateway Timeout fixes

Questions

Frequently asked questions

The latency argument is weaker, but the others still hold: origin offload, spike absorption, lower bandwidth costs and attack resilience apply regardless of geography.

It should not. The HTTP caching standard forbids shared caches from reusing responses to requests with an Authorization header unless the response explicitly allows it, and most proxy caches skip responses that set cookies. Mark personalised pages private or no-store anyway, so the outcome never depends on defaults.

Either purge it explicitly through the API or panel, or version the filename so a change produces a new URL. Versioning is more robust because it never requires a purge to succeed.

It depends on what you serve. A site made of fingerprinted assets and public pages can answer most requests from the edge, while an API or a logged-in application may legitimately cache little. Track the ratio by requests and by bytes separately, and investigate any drop after a deploy, which usually means a new cookie, Vary header or query string.

No. A CDN absorbs attack traffic aimed at cached URLs as a side effect, but uncached routes, cache-busting floods and network-layer attacks still reach the origin unless something filters them. DDoS protection is the part that identifies and drops that traffic.

Sources

Sources and verification

nginx cache configuration checked with nginx -t and run locally with nginx 1.22.1 to confirm a miss followed by a hit and that a response carrying Set-Cookie was not cached; the curl header check run with curl 7.88, Debian 12, October 2026.

  1. 01RFC 9111: HTTP Caching · IETF
  2. 02RFC 8246: HTTP Immutable Responses · IETF
  3. 03RFC 5861: HTTP Cache-Control Extensions for Stale Content · IETF
  4. 04RFC 9211: The Cache-Status HTTP Response Header Field · IETF
  5. 05Module ngx_http_proxy_module · nginx.org

Last updated