CDN Architecture
A Content Delivery Network places cache nodes (Points of Presence, PoPs) geographically close to users. Instead of every request traveling to your origin server, cached content is served from the nearest PoP.
User (Tokyo) → Cloudflare Tokyo PoP → (cache miss) → Origin (US-East)
User (Tokyo) → Cloudflare Tokyo PoP → (cache hit) → Response
Origin shield (available on Cloudflare, CloudFront, Fastly) adds a single centralized cache tier between PoPs and origin, collapsing cache misses:
PoP (Tokyo) ─┐
PoP (London) ─┤─→ Origin Shield (US-East) → Origin
PoP (Sydney) ─┘
Without origin shield, a cache miss from 50 PoPs simultaneously generates 50 origin requests. With origin shield, at most one.
Static Asset Caching
Static assets (JavaScript bundles, CSS files, images) should use immutable caching with content-addressed filenames. When the file changes, the filename changes — so the old URL is never invalid.
# Nginx: cache static assets for 1 year
location /static/ {
expires 1y;
add_header Cache-Control 'public, max-age=31536000, immutable';
add_header Vary Accept-Encoding;
gzip_static on;
}
The immutable directive tells browsers the content will never change at this URL — they can skip revalidation requests entirely.
<!-- Content-hashed filenames: change content → change URL -->
<link rel='stylesheet' href='/static/main.a3f9c2.css'>
<script src='/static/bundle.7d8e1b.js'></script>
Django WhiteNoise Static Files
# settings.py — WhiteNoise adds immutable headers automatically
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware',
...
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# Generates /static/main.HASH.css filenames automatically
HTML Caching
HTML pages usually contain personalized or frequently updated content. Use short TTLs with stale-while-revalidate to serve stale content instantly while refreshing in the background:
location / {
proxy_pass http://app_servers;
proxy_cache_valid 200 10m;
add_header Cache-Control 's-maxage=10, stale-while-revalidate=60';
}
This configuration means:
- CDN serves fresh cache for 10 seconds (
s-maxage=10) - Between 10s and 70s: CDN serves stale content AND triggers a background refresh
- After 70s: CDN must wait for a fresh response
For personalized pages (user dashboard, shopping cart), bypass the CDN cache entirely:
# Bypass cache if session cookie is present
proxy_cache_bypass $cookie_sessionid;
proxy_no_cache $cookie_sessionid;
Cloudflare Cache Rules
# Cloudflare Cache Rule — Cache HTML pages for 5 minutes
If: hostname equals example.com AND URI path does not contain /api/
Then: Cache eligibility = Eligible for cache
Edge TTL = 5 minutes
Browser TTL = 30 seconds
API Response Caching
API responses are often private (user-specific) or highly dynamic. The Vary header tells the CDN which request headers affect the response:
HTTP/1.1 200 OK
Cache-Control: public, max-age=60
Vary: Accept-Language, Accept-Encoding
This creates separate cache entries for each Accept-Language value — French and English users get different cached responses.
Cache Key Customization
CDNs default to caching by URL only. For API responses that vary by query parameter or custom header, customize the cache key:
# Cloudflare Workers — custom cache key
const cacheKey = new Request(
`${url.origin}${url.pathname}?currency=${url.searchParams.get('currency')}`,
request
);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
response = await fetch(request);
await cache.put(cacheKey, response.clone());
}
Bypass Rules for Private Data
# Never cache:
Cache-Control: private, no-store
# Cache at CDN but not browser:
Cache-Control: public, s-maxage=300, max-age=0
# Cloudflare bypass header (set by origin):
CF-Cache-Status: BYPASS
CDN Error Page Handling
When your origin returns a 5xx error (or is unreachable), the CDN can serve a custom error page from its edge cache rather than passing the error to the user.
Cloudflare Custom Error Pages
In the Cloudflare dashboard, upload HTML for specific error codes. The CDN serves these pages from its edge when:
- Origin returns 502/503/504
- Origin connection times out
- Always Online mode serves cached pages during origin outages
AWS CloudFront Custom Error Responses
{
"CustomErrorResponses": {
"Quantity": 2,
"Items": [
{
"ErrorCode": 503,
"ResponsePagePath": "/errors/503.html",
"ResponseCode": "503",
"ErrorCachingMinTTL": 10
},
{
"ErrorCode": 502,
"ResponsePagePath": "/errors/maintenance.html",
"ResponseCode": "503",
"ErrorCachingMinTTL": 5
}
]
}
}
Origin Failover
// CloudFront Origin Group with failover
{
"OriginGroupMembers": [
{"OriginId": "primary-us-east"},
{"OriginId": "failover-us-west"} // Activated on 5xx
],
"FailoverCriteria": {
"StatusCodes": [500, 502, 503, 504]
}
}
CDN Configuration Checklist
| Content Type | Cache-Control | CDN TTL | Browser TTL |
|---|---|---|---|
| JS/CSS (hashed) | `public, max-age=31536000, immutable` | 1 year | 1 year |
| Images | `public, max-age=86400` | 24h | 24h |
| HTML pages | `s-maxage=60, stale-while-revalidate=300` | 1 min | 0 |
| API (public) | `public, s-maxage=300` | 5 min | 0 |
| API (private) | `private, no-store` | Never | Never |
| robots.txt | `no-store` | Never | Never |