Static assets have appropriate Cache-Control headers
Why it matters
Static assets served without long-lived Cache-Control headers force every returning visitor to re-download CSS, JavaScript, and image files they already have — identical bytes transferred repeatedly for no reason. A 500KB bundle re-downloaded on every visit costs 500KB × daily_active_users in bandwidth per day. With Cache-Control: public, max-age=31536000, immutable, that same bundle is downloaded once per user per year. For users on metered connections or high-latency networks, the cache miss is also the difference between a 500ms and a 3-second load. ISO 25010 performance-efficiency.resource-utilization flags repeat downloads of identical resources as a direct waste.
Severity rationale
High because missing cache headers on static assets force full re-downloads on every page visit, wasting bandwidth and adding measurable latency for returning users.
Remediation
Configure Cache-Control: public, max-age=31536000, immutable for all hashed static assets, and max-age=0, must-revalidate for HTML files. On Vercel and Netlify, Next.js applies these headers to hashed chunks automatically — verify no override is negating the defaults.
// vercel.json — explicit headers for custom infrastructure
{
"headers": [
{
"source": "/_next/static/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "/(.*)\\.(js|css|woff2|png|webp|avif)$",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "/(.*)\\.html$",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" }
]
}
]
}
For custom Express servers: app.use('/static', express.static('public', { maxAge: '1y', immutable: true }))
Detection
-
ID:
static-cache-headers -
Severity:
high -
What to look for: Count all locations where cache headers could be configured: framework config (
next.config.*headers), deployment config (vercel.json,netlify.toml), middleware, and custom server files. For each, check theCache-Controlvalue for static assets (CSS, JS, images). Enumerate: "X cache configuration locations checked." For a deeper analysis of security headers including CSP and HSTS, the Security Headers Audit (security-headers) covers this in detail. -
Pass criteria: Static assets (JS, CSS, images) have
Cache-Control: public, max-age=31536000or similar long expiration (at least 604800 seconds / 1 week, ideally 31536000 / 1 year). HTML has short cache (max-age=0, must-revalidate) or no-cache. Pass by platform default when Vercel, Netlify, or similar managed hosting is detected —Cache-Control: public, max-age=31536000, immutableis applied to all hashed static chunks automatically without explicit configuration. Note this in the detail field as a platform-dependent pass (e.g.,"Pass: Vercel + Next.js applies long-lived cache headers to hashed static assets automatically."). -
Fail criteria: A non-managed hosting setup is detected (Dockerfile, custom server, bare AWS config, nginx config) and no explicit
Cache-Controlconfiguration is found for static assets. Or managed hosting is detected but explicit configuration actively overrides the defaults withmax-ageunder 604800 (1 week). -
Skip (N/A) when: Hosting platform truly cannot be determined AND no server configuration files exist (0 deployment config files found).
-
Detail on fail:
"0 of 3 cache configuration locations set long-lived headers — CSS and JS served with default short cache on custom Express server"or"vercel.json overrides default caching with max-age=3600 (1 hour) instead of 31536000 (1 year)" -
Remediation: Configure cache headers in deployment config:
{ "headers": [ { "source": "/(.*)\\.(js|css|woff2|png|jpg|webp)$", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] }, { "source": "/(.*)\\.(html)$", "headers": [ { "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" } ] } ] } -
Cross-reference: For security-related cache headers (preventing sensitive data caching), the Security Headers audit covers Cache-Control in the context of security.
External references
- iso-25010:2011 · performance-efficiency.resource-utilization — Resource Utilization
Taxons
History
- 2026-04-18·v1.0.0·Initial import from performance-load·automated