Long-term caching and persistent cache

webpack · Advanced · 12 min

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" and chunkIds: "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, and index.html without 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.

Check yourself

  1. Why extract the runtime with `runtimeChunk: 'single'`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: webpack.js.org/guides/caching/