Content Delivery Networks (CDN) Cheat Sheet
Explains how CDNs cache and serve content at the edge, key Cache-Control headers, example configuration, and the benefits of using one.
Core CDN Concepts
Terminology used across every CDN provider.
- Edge server / PoP- Point of Presence — a distributed server that caches and serves content near users
- Origin server- Your actual backend that the CDN pulls content from on a cache miss
- Cache hit/miss- Hit = edge had the content; Miss = it fetched and cached from origin
- TTL- Time to Live — how long an edge server keeps a cached object before revalidating
- Purge/Invalidation- Manually evicting cached content from edge nodes before its TTL expires
- Origin Shield- Extra caching layer between edges and origin to reduce origin load on misses
- Anycast routing- Routes a request to the nearest/healthiest edge location using one shared IP
Cache-Control Headers
Three common caching policies and conditional revalidation.
# Cache aggressively — for versioned/hashed static assets (immutable content)Cache-Control: public, max-age=31536000, immutable# Cache but revalidate — for HTML that changes but should still hit cacheCache-Control: public, max-age=0, must-revalidate# Never cache — for sensitive/dynamic API responsesCache-Control: private, no-store# Combine with ETag for conditional revalidationETag: "33a64df5"# Client's next request:If-None-Match: "33a64df5"# Server replies 304 Not Modified if unchanged (saves bandwidth)
Example CDN Configuration
A CloudFront-style distribution with a bypassed API path.
{ "Origins": [{ "DomainName": "origin.example.com", "Id": "primary-origin" }], "DefaultCacheBehavior": { "TargetOriginId": "primary-origin", "ViewerProtocolPolicy": "redirect-to-https", "CachePolicyId": "CachingOptimized", "Compress": true }, "CacheBehaviors": [ { "PathPattern": "/api/*", "TargetOriginId": "primary-origin", "CachePolicyId": "CachingDisabled" } ]}
Benefits & When to Use
Why almost every production site sits behind a CDN.
- Lower latency- Content served from a nearby edge instead of a distant origin server
- Reduced origin load- Cache absorbs repeat requests, protecting the origin from traffic spikes
- DDoS mitigation- CDNs absorb and filter large-scale traffic at the edge before it reaches origin
- Bandwidth savings- Origin egress traffic drops significantly as cache hit ratio increases
- Global availability- Multiple PoPs provide redundancy if one region has an outage
- TLS termination at the edge- Faster HTTPS handshakes closer to the user
stale-while-revalidate & Vary
Serving stale content instantly while refreshing in the background, and varying the cache key by request headers.
# Serve a stale copy immediately, revalidate in the background for up to 1 hourCache-Control: public, max-age=60, stale-while-revalidate=3600# If origin errors, keep serving the last good cached copy for up to a dayCache-Control: public, max-age=60, stale-if-error=86400# Cache key must fork on Accept-Encoding and a custom device header,# otherwise a gzip response could be served uncompressed to another clientVary: Accept-Encoding, X-Device-Type# Surrogate-Control lets the CDN cache longer than the browser doesSurrogate-Control: max-age=604800Cache-Control: no-cache
Edge Function (Cloudflare Worker)
Running logic at the PoP itself to rewrite requests or add auth before hitting cache or origin.
export default { async fetch(request, env, ctx) { const url = new URL(request.url); // A/B bucket assigned at the edge, no origin round-trip if (!request.headers.get('Cookie')?.includes('bucket=')) { const bucket = Math.random() < 0.5 ? 'a' : 'b'; const resp = await fetch(request); const res = new Response(resp.body, resp); res.headers.append('Set-Cookie', `bucket=${bucket}; Path=/`); return res; } // Cache API lets a Worker manage its own edge cache explicitly const cache = caches.default; let response = await cache.match(request); if (!response) { response = await fetch(request); ctx.waitUntil(cache.put(request, response.clone())); } return response; },};
Advanced Caching & Invalidation Strategies
Techniques beyond a basic TTL for keeping edge caches fresh and resilient.
- Surrogate keys / cache tags- Tag cached objects (e.g. by product ID) so a single purge call invalidates every related URL
- Tiered/shielded caching- A mid-tier regional cache sits between edge PoPs and origin to consolidate misses
- Cache stampede protection- Coalesce concurrent misses for the same key into one origin request (request collapsing)
- Negative caching- Briefly cache 404/error responses so a broken URL doesn't hammer the origin repeatedly
- Prefetch / prewarming- Push new deploy artifacts to edge caches proactively instead of waiting for first-request misses
- Cache key normalization- Strip tracking query params (utm_*) from the cache key so identical pages aren't cached N times
- Soft purge- Mark content stale (serve-while-revalidate) instead of hard-evicting it, avoiding a cold-cache spike
Signed URLs for Private Content
Restricting access to cached assets (e.g. paid video, private downloads) with a time-limited signature.
const crypto = require('crypto');function signCdnUrl(path, expiresInSeconds, secret) { const expires = Math.floor(Date.now() / 1000) + expiresInSeconds; const stringToSign = `${path}${expires}`; const signature = crypto .createHmac('sha256', secret) .update(stringToSign) .digest('base64url'); return `https://cdn.example.com${path}?expires=${expires}&sig=${signature}`;}// Edge validates: recompute HMAC, check signature match AND expires > now// A leaked signed URL only exposes content until it expires, unlike a// permanently public path
Tag-Based Cache Purge API
Invalidating every cached object associated with a surrogate key in one call, instead of purging URLs one by one.
# Tag responses at origin so the CDN can group them# Surrogate-Key: product-123 category-shoes# Purge everything tagged 'product-123' across all edge nodes globallycurl -X POST "https://api.cdn-provider.com/v1/purge" \ -H "Authorization: Bearer $CDN_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tags": ["product-123"]}'# Response includes a purge ID you can poll for completion status# since global purge propagation is eventually consistent (seconds, not instant)
Set a long max-age plus immutable on assets with content-hashed filenames (e.g. app.a3f9c2.js) so browsers and edge caches never revalidate them — then bust the cache simply by deploying a new build with a different hash, never by purging.