Production Infrastructure

CDN Configuration for Dynamic and Static Content

How to configure CDN caching rules for different content types — static assets (immutable), HTML (short TTL), API responses (private), and edge error pages.

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 TypeCache-ControlCDN TTLBrowser TTL
JS/CSS (hashed)`public, max-age=31536000, immutable`1 year1 year
Images`public, max-age=86400`24h24h
HTML pages`s-maxage=60, stale-while-revalidate=300`1 min0
API (public)`public, s-maxage=300`5 min0
API (private)`private, no-store`NeverNever
robots.txt`no-store`NeverNever

Related Protocols

Related Glossary Terms

More in Production Infrastructure