contenthash vs chunkhash: handling stale cache when modules move between chunks
0 reputation · 07 Sept 2020, 17:37 UTC
0 reputation · 07 Sept 2020, 17:37 UTC
The goal is to maintain effective long‑term caching of assets when a module is moved from an entry‑point specific chunk to a shared chunk created by the splitChunks plugin.
With output.filename using [contenthash] the hash reflects only the file’s own content, while [chunkhash] ties the hash to the entire chunk’s contents. When a module shifts chunks, the old chunk’s filename may stay unchanged under contenthash, potentially leaving an orphaned file that browsers could still request if the HTML still references it. The webpack documentation does not specify whether the build process automatically deletes or overwrites such stale chunk assets, or whether developers must manually clean the output directory to avoid serving outdated code.
34775 reputation · 07 Sept 2020, 20:06 UTC
Webpack does not automatically delete previously emitted chunk files when a module moves to a different chunk. Stale assets remain in the output directory until you explicitly clean it. The choice between [contenthash] and [chunkhash] does not by itself create or prevent orphaned-file requests; orphaned files are only served if something (old HTML, a service-worker precache manifest, a hard-coded URL, or a browser cache) still requests the old filename.
Each Webpack compilation emits only the assets that exist in the current module graph. When splitChunks moves a module from an entry-specific chunk into a shared chunk, the old chunk is still emitted (now without that module) and the new shared chunk is emitted. Both receive new hashes because their contents changed. The previous build's files, however, are left untouched on disk. HtmlWebpackPlugin rewrites the HTML to point at the new hashed filenames, but it never deletes the old ones.
// webpack.config.js
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true // removes output.path before each emit
}
};The clean option deletes the entire output.path directory before writing new assets. Ensure that directory contains only build output.
output.clean is unavailable// Option A: clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [new CleanWebpackPlugin()]
};# Option B: pre-build script (cross-platform)
npx rimraf dist
# or
rm -rf dist| Aspect | [contenthash] | [chunkhash] |
|---|---|---|
| Scope | Per emitted file (JS, CSS, etc.) | Per chunk (all files in that chunk share one hash) |
| Stability when unrelated sibling changes | Unaffected — only the changed file gets a new hash | All files in the chunk get a new hash |
| Orphan risk when module moves | Identical — both old and new chunks receive new hashes because their byte content changed | Identical — same reason |
Use [contenthash] for finer-grained invalidation (e.g., extracted CSS stays cached when only JS changes). Use [chunkhash] if you want a single version token per logical chunk. Neither eliminates the need to clean dist.
npx webpack --version (output.clean requires Webpack 5.20+).splitChunks cache group, build twice, and list dist/ — you should see only the latest hashed files if cleaning works.<script>/<link> points to a file that currently exists in dist/.What Webpack version are you running, and does your deployment pipeline already include a dist cleanup step (e.g., clean: true, clean-webpack-plugin, or a CI rm -rf)? The answer determines whether you need to add configuration or simply verify the existing cleanup runs on every build.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 07 Sept 2020, 18:30 UTC
One practical caveat with output.clean: true (webpack 5.20+): it removes the entire output.path directory before emitting. If that folder also holds non-build artifacts—uploaded media, server configs, or CI manifests—they are deleted too. In shared setups, isolate bundles into a dedicated subdirectory or use CleanWebpackPlugin with narrow patterns instead.
To confirm stale chunks are actually gone after moving a module, compare the assets keys from webpack --json across two builds. This verifies emitted filenames without relying on manual directory listings or HTML inspection.