Hashed assets are designed for aggressive caching — the filename changes only when the file content changes, so browsers can safely cache them forever. Without immutable, max-age=31536000, browsers re-validate these files on every visit, adding a round-trip for each asset even when the content is identical. Conversely, if HTML files are cached with a long max-age, users miss updates entirely until the cache expires. ISO 25010:2011 time-behaviour frames the latency; the business impact is repeat visitors waiting on unnecessary network round-trips.
Critical because misconfigured cache headers force re-validation of every asset on every visit, eliminating the performance gains that content-hashed filenames are specifically designed to provide.
Set Cache-Control: public, immutable, max-age=31536000 for hashed assets and no-cache for HTML in your deployment config.
// vercel.json
{
"headers": [
{
"source": "/_next/static/(.*)",
"headers": [{ "key": "Cache-Control", "value": "public, immutable, max-age=31536000" }]
},
{
"source": "/(.*)\.html",
"headers": [{ "key": "Cache-Control", "value": "public, no-cache" }]
}
]
}
ID: performance-deep-dive.caching-cdn.cache-control-hashed-assets
Severity: critical
What to look for: Count all cache-related HTTP headers in deployment config. Enumerate the Cache-Control values for hashed assets vs. HTML files. Examine the deployment config (vercel.json, netlify.toml, _headers, or equivalent) and HTTP headers sent by the application. For hashed/immutable assets (files with content hash in name like app.a1b2c3d4.js), check for Cache-Control: immutable, max-age=31536000 (1 year). For HTML files, check for Cache-Control: no-cache or must-revalidate.
Pass criteria: Hashed assets (JavaScript, CSS, images with content hash filenames) are cached with immutable and max-age=31536000 (1 year). HTML files use no-cache or must-revalidate to ensure latest version is checked before serving. Hashed assets must have max-age of at least 31536000 (1 year).
Fail criteria: Hashed assets use short cache times (max-age < 31536000) or no cache headers. HTML files are cached with max-age > 0 without no-cache.
Skip (N/A) when: Never — HTTP caching is fundamental for performance.
Do NOT pass when: Cache-Control headers exist but are set on the wrong path patterns, caching HTML with immutable or caching unhashed assets with long max-age.
Cross-reference: For asset hashing strategy, see the asset-content-hash check. For CDN cache hit ratio, see cdn-cache-hit-ratio.
Detail on fail: "Hashed assets cached with max-age=86400 (1 day) instead of 1 year" or "HTML files cached with Cache-Control: max-age=3600, preventing users from getting updates"
Remediation: Configure HTTP caching headers on your deployment platform properly.
// vercel.json — cache headers
{ "headers": [{ "source": "/_next/static/(.*)", "headers": [{ "key": "Cache-Control", "value": "public, immutable, max-age=31536000" }] }] }