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:
- Open Chrome DevTools > Network tab.
- Check the
Sizecolumn for(disk cache)or(memory cache). - Confirm
Status Codeis200 OK(not304 Not Modified). - 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
Acceptheader negotiation at the edge. Serve WebP/JPEG with the samemax-agebut a distinct URL hash. Log406/415responses 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.