# Automatic SRI hashes with webpack and Vite (/en/blog/sri-webpack-vite)



A bundler emits dozens of hashed chunk files, and their names and contents change on every build. Computing a Subresource Integrity (SRI) hash for each one by hand is not realistic. The fix is to let the build do it: both webpack and Vite can write the `integrity` attribute onto the `<script>` and `<link>` tags they inject, and keep each hash in sync with the chunk it covers.

The short version: add the [`webpack-subresource-integrity`](https://github.com/waysact/webpack-subresource-integrity) plugin for [webpack](https://webpack.js.org/concepts/), or a community plugin such as [`@small-tech/vite-plugin-sri`](https://github.com/small-tech/vite-plugin-sri) for [Vite](https://vite.dev/guide/), and the integrity attributes appear automatically. One catch matters: the bundler only hashes the chunks it emits. The third-party scripts you load from a CDN are not in the build, so they get no integrity, and you still have to watch them yourself.

## Why automate it [#why-automate-it]

SRI is tied to the exact bytes of a file. Change the file, regenerate the hash, or the browser blocks the load. For your own bundled output, the bytes change on every meaningful build, and the file names are content-hashed too, so a hand-maintained list of hashes would be wrong within one commit.

Wiring SRI into the build removes that maintenance entirely for your own assets. The plugin hashes each chunk after it is emitted and writes the matching `integrity` attribute into the HTML or asset manifest, so the hash and the file always agree. [How to generate a Subresource Integrity (SRI) hash](/en/blog/generate-sri-hash) covers the manual route for one-off files; this post is the build-time route for many.

## Webpack [#webpack]

Install the plugin and add it to your plugins list. It reads which hash functions you want and writes the `integrity` attribute onto the tags injected by `html-webpack-plugin`.

```js title="webpack.config.js"
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");

module.exports = {
  output: {
    crossOriginLoading: "anonymous", // required for SRI to be checked
  },
  plugins: [
    new SubresourceIntegrityPlugin({
      hashFuncNames: ["sha384"],
    }),
  ],
};
```

Two pieces are easy to miss. You must set `output.crossOriginLoading` to `"anonymous"`, because the browser cannot verify an integrity hash on a cross-origin chunk fetched without CORS, and a bundle served from a CDN is cross-origin. The plugin emits a warning if you forget. Second, the plugin integrates with `html-webpack-plugin` to stamp the attributes onto the generated tags; if you inject scripts another way, you read the hashes from the asset manifest instead.

### The code-splitting gotcha [#the-code-splitting-gotcha]

This is the reason a generic "hash the files" script is not enough. Webpack code-splitting produces dynamically imported chunks that the runtime loads on demand, not tags in your HTML. Those chunks reference each other, so a child chunk's hash is part of its parent's content, and computing the hashes in the wrong order produces values that never match.

`webpack-subresource-integrity` handles this. It hooks into the webpack runtime so dynamically loaded chunks carry and verify integrity too, and it orders the hashing so cross-chunk references resolve correctly. A naive post-build hashing pass over the output folder cannot do that, which is why the plugin exists rather than a shell one-liner.

## Vite [#vite]

Vite ships no first-party SRI, so use a community plugin. `@small-tech/vite-plugin-sri` computes the hashes at build time and injects the `integrity` attributes into the emitted `index.html`.

```js title="vite.config.js"
import { defineConfig } from "vite";
import sri from "@small-tech/vite-plugin-sri";

export default defineConfig({
  plugins: [sri()],
});
```

The plugin runs during the build and rewrites the script and stylesheet tags with their integrity values. Check the plugin's own readme for how it handles your asset layout and whether it covers dynamically imported chunks, since the Vite ecosystem has several SRI plugins with different scopes. As with webpack, assets served cross-origin still need the browser to fetch them with CORS for the check to run.

## The gap, third-party CDN scripts [#the-gap-third-party-cdn-scripts]

Here is the part teams miss. The bundler hashes **your** chunks. It does not hash the scripts you pull straight from a third-party CDN, a tag manager, an analytics snippet, a payment widget, a chat embed, because those never pass through the build.

Those third-party scripts are exactly the ones worth pinning, since you do not control the server that serves them. The bundler cannot help here, so you handle them separately:

* Add `integrity` and `crossorigin` to each third-party tag by hand, generating the value with the [SRI generator](/tools/sri-hash), and pin a versioned URL so the bytes do not move under you.
* Or require integrity across the whole document with the [`Integrity-Policy`](/en/docs/web-security/policies/integrity-policy) header, which blocks in-scope scripts that load without integrity metadata (recently available across current Chrome, Firefox, and Safari).

Either way, you first have to know which third-party scripts you load, and that list changes as marketing and product add tags over time. CentralCSP builds a [script inventory](/platform/supply-chain) of every script running on your pages from CSP hash reports, so a new or changed third-party file shows up as a finding instead of slipping in unnoticed. That is the visibility the bundler cannot give you, because the bundler only sees what it built.

## Putting it together [#putting-it-together]

* Wire SRI into the build for your own assets: `webpack-subresource-integrity` for webpack, a Vite SRI plugin for Vite. Set cross-origin loading to `anonymous`.
* Let the plugin handle code-split dynamic imports; do not roll your own post-build hashing pass.
* Hash third-party CDN scripts separately, and track which ones you load so the list does not drift.

To see what third-party code is actually running on your pages before you start pinning it, [start free](/register) and let CentralCSP inventory your client-side scripts.

## Sources [#sources]

* [webpack-subresource-integrity, GitHub](https://github.com/waysact/webpack-subresource-integrity)
* [webpack-subresource-integrity, npm](https://www.npmjs.com/package/webpack-subresource-integrity)
* [@small-tech/vite-plugin-sri, GitHub](https://github.com/small-tech/vite-plugin-sri)
* [MDN, Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)

## Related [#related]

* [How to generate a Subresource Integrity (SRI) hash](/en/blog/generate-sri-hash)
* [Subresource Integrity (SRI) explained](/en/blog/subresource-integrity-sri)
* [Subresource Integrity (SRI) reference](/en/docs/web-security/other/subresource-integrity)
