# Static assets use content-hash filenames for cache busting

- **Pattern:** `ab-002071` (`performance-load.caching.immutable-hashing`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002071
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002071)

## Why it matters

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.

## Severity rationale

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.

## Remediation

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.

```ts
// 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.

## Detection

- **ID:** `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:

  ```ts
  // 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'
      }
    }
  }
  ```

## External references

- iso-25010 performance-efficiency.resource-utilization

Taxons: performance

HTML version: https://auditbuffet.com/patterns/ab-002071
