Fix Cache-Control Headers โ Why Your CDN Isn't Caching
Updated April 2026
If Cache-Control is missing, CDNs default to not caching โ or make inconsistent decisions based on other headers. Explicit Cache-Control headers give you full control over what gets cached and for how long.
Check your current headers first
curl -I https://yoursite.com/ curl -I https://yoursite.com/static/app.js curl -I https://yoursite.com/api/data
If you see no Cache-Control or Cache-Control: no-cache on static assets, your CDN is not caching them โ every request goes to your origin server.
Cache-Control by resource type
Versioned static assets (JS, CSS, fonts)
These have content hashes in the filename (app.abc123.js). The content never changes, so cache them forever:
Cache-Control: public, max-age=31536000, immutable
immutable tells browsers not to revalidate during max-age, even on back/forward navigation. Removes unnecessary conditional requests.
Non-versioned static assets (images, logo.png)
Cache for a week or month, but allow revalidation:
Cache-Control: public, max-age=604800, stale-while-revalidate=86400
HTML pages
Cache briefly or not at all โ HTML references your versioned assets, so you need users to get fresh HTML when you deploy:
# Option 1 โ no CDN cache (always fresh) Cache-Control: public, max-age=0, must-revalidate # Option 2 โ CDN cache with instant invalidation Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400
API responses (non-authenticated)
# Cache for 60 seconds at CDN, serve stale for 24h while revalidating Cache-Control: public, s-maxage=60, stale-while-revalidate=86400
API responses (authenticated)
# Never cache at CDN โ must use private Cache-Control: private, no-store
Config by stack
Nginx
location ~* \.(js|css|woff2|png|jpg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, max-age=31536000, immutable" always;
}
location ~* \.html$ { add_header Cache-Control "public, max-age=0, must-revalidate" always;
}
Vercel (vercel.json)
{ "headers": [ { "source": "/static/(.*)", "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }] }, { "source": "/(.*\.html)", "headers": [{ "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" }] } ]
}
Cloudflare
Cloudflare Dashboard โ Caching โ Cache Rules โ Create rule โ File extension matches js,css,png,jpg,woff2 โ Edge Cache TTL: 1 year.
Use EdgeFix to audit your current caching headers and see exactly what the CDN is receiving for each resource type.
Run a live PageSpeed audit โ SpeedFixer