Without content-hash filenames, long-lived cache headers create a contradiction: you want assets cached forever, but you also need users to get updated JavaScript after a deploy. Non-hashed filenames like main.js force a compromise — either short caches that cause repeat downloads, or long caches that serve stale JavaScript after deploys. Content hashing resolves this: main.a3f92b.js is immutable by definition — the hash changes whenever the content changes, guaranteeing users always get the latest code on deploy while keeping the file cached indefinitely between deploys. ISO 25010 performance-efficiency.resource-utilization requires that caching strategies actually eliminate redundant transfers.
Medium because non-hashed filenames force a cache-duration tradeoff that either penalizes return visitors with re-downloads or risks serving stale JavaScript after deploys.
Enable content hashing in your build configuration. Next.js generates hashed filenames for all chunks by default — no Webpack configuration required. For Vite or custom Webpack setups, configure the output filename templates.
// Next.js — no configuration needed
// Build output: .next/static/chunks/main-a3f92b8d.js (hashed automatically)
// Vite — vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: '[name].[hash].js',
assetFileNames: '[name].[hash][extname]'
}
}
}
})
// Webpack — webpack.config.js
module.exports = {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js'
}
}
Verify by running your build and confirming filenames in the output directory contain hash strings.
ID: performance-load.caching.immutable-hashing
Severity: medium
What to look for: Count all JS and CSS output files in the build directory. For each, check whether the filename contains a content hash (pattern: name.[hash].ext or name-[hash].ext where hash is at least 8 hex/base64 characters). Enumerate: "X of Y build output files use content-hashed filenames."
Pass criteria: At least 90% of JS and CSS build output files have content hashes in filenames (e.g., main.[hash].js). No more than 1 static asset lacks a hash (entry HTML files are exempt). When content changes, the filename changes, busting the cache automatically. Vercel + Next.js deployment generates content-hashed filenames for all built chunks automatically — no Webpack or framework configuration is required. If the detected hosting is Vercel and the framework is Next.js, this check passes by platform default. Note this in the detail field as a platform-dependent pass (e.g., "Pass: Next.js build generates content-hashed filenames for all static chunks by default."). This pass does not apply if deploying to custom infrastructure where build output filename templates need explicit configuration.
Fail criteria: 2 or more static assets (JS, CSS) use non-hashed names (e.g., main.js, styles.css). Updates require manual cache-busting strategies or long-lived caches serve stale content.
Skip (N/A) when: Framework handles hashing automatically and evidence of hashed output is present in the build directory (verified by inspecting at least 3 output filenames).
Detail on fail: "0 of 8 JS/CSS files in build output have content hashes — main.js, vendor.js, styles.css all lack hash suffixes" or "2 of 10 build files lack hashes — app.js and global.css use non-hashed names despite Webpack config available"
Remediation: Configure hashing in build config:
// next.config.ts (automatic in Next.js)
// No config needed — Next.js hashes output files automatically
// Webpack
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js'
}
// Vite
build: {
rollupOptions: {
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: '[name].[hash].js'
}
}
}