Users should only re-download what changed, and developers shouldn't wait for unchanged work.
Two different kinds of caching matter with webpack.
Browser caching of outputs. With [contenthash] in filenames, unchanged files keep their names and stay cached. To keep hashes truly stable:
optimization.runtimeChunk: "single"moves webpack's small runtime (which contains the chunk-to-filename manifest) into its own file. Otherwise, adding a lazy chunk changes the manifest inside your main bundle and its hash.optimization.moduleIds: "deterministic"andchunkIds: "deterministic"(production defaults in webpack 5) keep ids stable when unrelated modules are added.- Split rarely changing vendor code into its own chunk.
- Serve hashed files with
Cache-Control: max-age=31536000, immutable, andindex.htmlwithout caching.
Build caching. cache: { type: "filesystem" } persists the module and chunk caches to node_modules/.cache/webpack, making second and later builds dramatically faster, including in CI if you cache that folder. Invalidate correctly with cache.buildDependencies: { config: [__filename] } so config changes reset the cache; cache.version lets you bump it manually. In development mode the default is an in-memory cache.
If builds behave strangely after dependency upgrades, delete the cache folder first.
Example
// webpack.config.js (production excerpt)
module.exports = {
mode: "production",
output: {
filename: "[name].[contenthash].js",
chunkFilename: "[name].[contenthash].chunk.js",
},
optimization: {
runtimeChunk: "single", // runtime.[hash].js holds the manifest
moduleIds: "deterministic",
splitChunks: {
cacheGroups: {
vendor: { test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all" },
},
},
},
cache: {
type: "filesystem", // node_modules/.cache/webpack
buildDependencies: { config: [__filename] }, // config change = fresh cache
},
};Remember
- contenthash + runtimeChunk + deterministic ids = stable hashes
- Vendor chunks change rarely and stay cached
cache: { type: 'filesystem' }speeds up rebuilds and CI- Declare buildDependencies so config changes invalidate the cache
Watch out for
- Leaving the runtime in the main chunk, so every new lazy route changes the main bundle's hash.