CDN Production Engineering
The configuration knobs, failure modes, and operational habits that separate a CDN that theoretically works from one that reliably serves production traffic.
Cache Strategy
A cache strategy is the set of decisions about what to cache, for how long, and under what conditions — made deliberately, per content type, not left to defaults.
Different asset classes need different strategies: versioned static bundles can cache forever; HTML shells might cache for seconds with revalidation; personalized API responses might never be cached at all. A good strategy maps each route or content type to an explicit policy rather than relying on a single global default.
Cache everything by default
Some CDNs support a mode where all responses are cached unless explicitly marked otherwise — powerful, but dangerous if origin routes don't set explicit Cache-Control headers.
Tiered caching
A mid-tier cache sits between edge PoPs and the origin, so a miss at one edge doesn't always trigger a full origin trip — it can be satisfied by the tier cache instead, further reducing origin load.
Cache-Control Configuration
Cache-Control is the primary contract between your origin and every downstream cache — get the directives right and everything downstream behaves correctly.
public / private
public allows any cache (CDN, browser, intermediate proxy) to store the response. private restricts caching to the end-user's browser only — critical for personalized content that shouldn't sit in a shared CDN cache.
max-age vs s-maxage
max-age applies to all caches including the browser. s-maxage applies specifically to shared caches (CDNs) and takes precedence over max-age there — letting you set a longer edge TTL than browser TTL.
no-cache vs no-store
no-cache allows caching but requires revalidation with the origin before serving (via ETag/Last-Modified). no-store forbids caching entirely, anywhere.
| Response | Example Cache-Control | Expected CDN behaviour |
|---|---|---|
Hashed static asset (app.a1b2c3.js) | public, max-age=31536000, immutable | Cached for the full year at the edge and in browsers; every request after the first is a HIT. |
| HTML shell | public, max-age=0, s-maxage=60, stale-while-revalidate=30 | Browsers revalidate every time; the edge reuses it for 60 s, then may serve it stale for up to 30 s while refreshing (support for stale-while-revalidate varies by CDN). |
| Authenticated API response | private, no-store | Never stored in a shared cache; always goes to the origin (a MISS/BYPASS in the cache-status header). |
$ curl -sI https://cdn.example.com/assets/app.a1b2c3.js
HTTP/2 200
cache-control: public, max-age=31536000, immutable
age: 4120
x-cache: HITTTL Configuration
Choosing the right TTL per asset type is one of the highest-leverage CDN decisions you'll make.
Long TTL + versioned filenames
For hashed static assets (app.a1b2c3.js), set TTL to a year (max-age=31536000, immutable) — the filename itself changes on every deploy, so staleness is impossible.
Short TTL for semi-dynamic content
A product listing or homepage might use a 10–60 second TTL — short enough that changes propagate quickly, long enough to absorb the vast majority of concurrent requests.
Origin overrides at the edge
Most CDNs let you set a minimum or maximum TTL at the edge configuration level, overriding what the origin sends — useful as a safety net against a misconfigured origin sending max-age=0 everywhere.
Cache Invalidation
Invalidation is an operational tool, not a design pattern — lean on versioned URLs first, and reserve purges for emergencies.
Purge by URL
Remove a single cached object across all edge locations — the most surgical option, but doesn't scale to bulk changes.
Purge by tag / surrogate key
Tag related responses (e.g., all pages referencing a product) with a shared key at cache time, then purge everything under that key in one call — essential for content-managed sites where one change affects many cached pages.
Wildcard purge
Purge everything matching a path pattern (/blog/*) — powerful but slow to propagate and easy to overuse; treat as a last resort.
Cache Keys
The cache key configuration is where hit-ratio optimization and correctness pull in opposite directions — tune it carefully.
Normalizing query parameters
Strip tracking parameters (utm_source, fbclid) from the cache key so semantically identical requests share one cache entry instead of fragmenting into thousands of near-duplicate entries.
Including headers deliberately
Only include a header in the cache key (via Vary) when the response genuinely differs by that header — every additional Vary dimension multiplies the number of cached variants.
Dynamic Content at the Edge
Modern CDNs do more than cache static files: they can micro-cache read-heavy responses, run logic at the edge, and accelerate uncached requests on the way back to the origin.
Micro-caching
Cache dynamic API or HTML responses for a very short window (1–30 seconds) — often short enough that users do not notice staleness, but long enough to absorb bursts of identical requests during traffic spikes.
Origin steering / dynamic acceleration
Even when a response is not cacheable, many CDNs still terminate client TLS nearby, reuse warm connections to origin, route traffic over the provider's optimized backbone, and fail over between healthy origins or shield tiers. Dynamic traffic still benefits from the CDN even without an edge cache hit.
Edge compute / edge functions
Some CDNs run small pieces of application logic directly at the edge (redirects, A/B test bucketing, request rewriting, authentication checks, even full server-rendered pages) — reducing origin round trips for logic that doesn't need the full origin stack.
Origin Shielding
Origin shielding adds a designated shield tier or PoP between many edge locations and the origin, so cache misses are consolidated before they fan into the backend.
Without shielding, multiple regional PoPs can all miss on the same newly popular object and hit the origin simultaneously — a thundering herd from the backend's point of view. With a shield, edge PoPs forward misses to the shield layer first; the shield fetches the object once from the origin, caches it, and then serves other PoPs from there. Some providers implement this as a specific shield PoP, others as a tiered cache layer, but the goal is the same: collapse many outer misses into far fewer origin fetches.
CDN Security
Sitting on every request makes the CDN a natural security enforcement point, not just a performance layer.
TLS termination
Terminate client TLS at the edge and often re-encrypt to the origin, reducing user-to-edge handshake distance while centralizing certificate management and policy enforcement.
Web Application Firewall (WAF)
Filter malicious request patterns (SQLi, XSS, known bad bot signatures) at the edge, before they ever reach application servers.
Signed URLs / tokens
Restrict access to protected content (paid media, private downloads) using time-limited signed URLs validated at the edge — no need to hit the origin to authorize every request.
DDoS Protection
A globally distributed CDN is one of the most effective defenses against volumetric DDoS attacks — by design, it can absorb far more traffic than any single origin.
Anycast absorption
Because anycast spreads traffic across every PoP globally, a volumetric attack gets naturally distributed across the entire network instead of concentrating on one target.
Rate limiting at the edge
Apply per-IP or per-token request limits before traffic ever reaches the origin, blocking abusive clients at the network edge.
Challenge pages / bot mitigation
Serve CAPTCHAs or JS challenges to suspicious traffic patterns at the edge, filtering out automated attacks while letting legitimate users through unaffected.
Monitoring
The handful of metrics that tell you whether a CDN is actually doing its job.
Cache-hit ratio
The headline metric — percentage of requests served from cache. A sudden drop usually points to a TTL, cache-key, or content-type regression.
Origin request rate
How many requests actually reach the origin per second — the metric that most directly reflects the CDN's protective value.
Edge latency (p50/p95/p99)
Response time as measured at the edge, broken down by region — reveals whether specific PoPs or regions are underperforming.
Error rate by status code
Track 5xx rates separately at the edge vs the origin — a spike in edge-originated 5xx often points to a CDN configuration issue rather than an application bug.
Common Production Issues
The recurring ways CDN configurations go wrong in production.
Cache poisoning via unkeyed inputs
Caching a response that varies by a header or parameter not included in the cache key — one malicious or malformed request gets cached and served to every subsequent user.
Thundering herd on cache expiry
A popular object's TTL expires and many concurrent requests all trigger simultaneous origin fetches instead of one request populating the cache while others wait — mitigated by request collapsing.
Stale content after deploy
A deploy ships new content but the CDN keeps serving the previous version until TTL expiry because invalidation wasn't triggered as part of the pipeline.
Overly aggressive caching of error responses
Caching a 500 or 502 response with a long TTL means users keep seeing the error long after the origin has recovered.
Debugging
The practical checklist for figuring out why a CDN is behaving unexpectedly.
Read the cache status header
Most CDNs expose a response header (X-Cache: HIT/MISS, CF-Cache-Status, etc.) — always check this first before assuming a caching bug.
Check Cache-Control on the actual response
Use browser devtools or curl -I to inspect the real headers the origin sent — the CDN can only obey what it's told.
Test from multiple regions
A caching issue can be region-specific — one PoP might hold a stale or corrupted cache entry while others are fine. Use a multi-region testing tool rather than trusting a single request from one location.
Bypass the cache to isolate the origin
Add a cache-busting query parameter or use an explicit bypass header/path to rule the CDN in or out — if the bug reproduces with cache bypassed, it's an origin issue, not a CDN caching issue.
- 01Set explicit Cache-Control at the origin per response class instead of relying on CDN dashboard defaults; versioned assets get
immutableyear-long TTLs. - 02Use
s-maxageto give the edge a different TTL from browsers, and prefer versioned URLs over purges for routine deploys. - 03Enable origin shielding for origins that can't absorb a cold-cache fan-out, and lock the origin down with the CDN's IP ranges plus a shared secret or mutual TLS.
- 04Cap the TTL on 5xx responses to seconds so a transient origin failure doesn't become a cached outage.
- 05Alert on cache-hit ratio against a baseline; a regression in TTL, cache key or Vary usually shows up there before origin load does.