Best practices for setting max-age on CDN media assets

The Immutable Directive: Why max-age=31536000 Is the Baseline

Modern CDNs and browsers treat media assets as immutable when paired with content-hashed URLs. A one-year max-age eliminates revalidation overhead, directly improving Time to First Byte (TTFB) and reducing origin load. For comprehensive header strategies, see Cache-Control Headers for Image and Video Assets.

Cache-Control: public, max-age=31536000, immutable

Tradeoff: The immutable directive prevents browsers from issuing conditional If-Modified-Since or If-None-Match requests during page reloads. This requires strict build pipeline discipline: if your hashing algorithm fails to update on content changes, users will receive stale media for up to 365 days. Validate your pipeline’s hash generation before enabling immutable.

Implementation Workflow: CLI, HTML, and Service Worker

Deploying aggressive caching requires synchronized asset versioning. Use build-time hashing (Vite, webpack, or equivalent) to generate fingerprinted URLs. Verify headers via CLI before deployment.

1. Pre-Deployment CLI Verification

# Verify exact header response before pushing to production
curl -sI https://cdn.yourdomain.com/assets/hero.abc123.webp \
  | grep -iE '(cache-control|etag|vary)'

Tradeoff: If ETag is present alongside max-age, browsers may still validate on manual reload. Strip ETag at the CDN edge for hashed assets to enforce pure max-age caching and eliminate 304 round trips.

2. HTML Preload & Delivery

<!-- Preload LCP media with fingerprinted URL -->
<link rel="preload" href="/media/lcp-image.8f4d2a.avif"
      as="image" type="image/avif" crossorigin="anonymous">

<picture>
  <source srcset="/media/lcp-image.8f4d2a.avif" type="image/avif">
  <source srcset="/media/lcp-image.8f4d2a.webp" type="image/webp">
  <img src="/media/lcp-image.8f4d2a.jpg" alt="Hero visual"
       loading="eager" fetchpriority="high">
</picture>

Always include crossorigin="anonymous" for cross-origin CDNs to prevent double-fetching caused by CORS cache partitioning.

3. Service Worker Cache Interception

self.addEventListener('fetch', (e) => {
  // Match fingerprinted media paths (8-char hex hash before the extension)
  if (e.request.url.includes('/media/') &&
      /\.[a-f0-9]{8}\.(avif|webp|jpg|mp4|webm)$/.test(e.request.url)) {
    e.respondWith(
      caches.match(e.request).then(r => r || fetch(e.request))
    );
  }
});

Tradeoff: Service workers add fine-grained control but introduce complexity in cache lifecycle management. Use cache.addAll() during the install phase for critical media, and implement version-based cache naming to purge stale entries across deploys.

Expected Performance Deltas & Validation

Metric Expected Improvement Validation Method
TTFB per asset 40–60ms reduction Chrome DevTools > Network > Timing tab
LCP 15–30% decrease WebPageTest or Lighthouse
Edge Cache Hit Ratio 95–99% CDN dashboard analytics
Origin Bandwidth 70–85% reduction Origin access logs / CloudWatch

Validation Workflow:

  1. Open Chrome DevTools > Network tab.
  2. Check the Size column for (disk cache) or (memory cache).
  3. Confirm Status Code is 200 OK (not 304 Not Modified).
  4. Disable cache in DevTools and reload to verify fallback behavior and origin response headers.

Failure Recovery & Edge Cases

If a deployed asset has a defect, immediate cache invalidation is required. Use CDN purge APIs for targeted removal.

# Cloudflare API: purge exact URL
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
  -H "Authorization: Bearer {API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://cdn.yourdomain.com/media/lcp-image.8f4d2a.avif"]}'

Safety Net Configuration:

Cache-Control: public, max-age=31536000, immutable, stale-while-revalidate=86400

stale-while-revalidate allows the CDN to serve a slightly stale asset while fetching the updated version in the background. Never rely on no-cache alone for versioned media; instead, deploy a new hashed URL and update all references atomically.

Edge Case Mitigation:

  • Stale content: Trigger CDN tag-based or exact-URL purge. Deploy a new fingerprinted asset and update HTML references atomically.
  • Broken fallback format: Implement Accept header negotiation at the edge. Serve WebP/JPEG with the same max-age but a distinct URL hash. Log 406/415 responses for pipeline debugging.
  • Cache poisoning risk: Enforce Vary: Accept, Accept-Encoding. Strip query strings at the CDN level for static media paths.

For architectural patterns, see Core Media Fundamentals & Next-Gen Formats.