Web Security

How Subresource Integrity (SRI) Helps Protect Your Website

Every external script and stylesheet your site loads is a trust decision. Subresource Integrity lets you verify that what the CDN delivers is exactly what you expect, and block it if it is not. This guide covers how SRI works, how to generate hashes, and how to integrate it into your workflow.

SiteSecurityScore Team·10 min read·Updated Apr 11, 2026
Abstract digital fingerprint pattern representing cryptographic hash verification

Most modern websites load JavaScript libraries, CSS frameworks, and fonts from content delivery networks. jQuery from a CDN, Bootstrap from another, Google Fonts from a third. Each of these external files runs with full access to your page. If an attacker compromises the CDN or injects malicious code into one of those files, every site loading it becomes a target.

Subresource Integrity is the browser's built in defense against this scenario. It lets you attach a cryptographic fingerprint to each external resource. The browser downloads the file, computes its own fingerprint, and compares the two. If they match, the file loads normally. If they don't, the browser blocks it entirely.

This guide explains what SRI protects against, how to generate and apply integrity hashes, and how to keep them updated as dependencies change.

What is Subresource Integrity#

Subresource Integrity (SRI) is a W3C specification that allows browsers to verify that a file fetched from an external server has not been modified. When you add an integrity attribute to a <script> or <link> tag, you are telling the browser the exact hash the file should produce. If the downloaded content produces a different hash, the browser refuses to use it.

The concept is similar to checking a file's checksum after downloading software. You compare the hash the publisher provides against the hash you compute locally. If they differ, you know the file was altered in transit or at the source. SRI automates this check for every page load, with the browser doing the verification before the code can run.

Script tag with SRI
<script src="https://cdn.example.com/library-3.6.0.min.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8w"
  crossorigin="anonymous"></script>

Two attributes work together here. The integrity attribute contains the expected hash, prefixed with the algorithm name (sha256, sha384, or sha512). The crossorigin="anonymous" attribute tells the browser to make a CORS request so it can read the response bytes and compute the hash. Without CORS, the browser treats cross-origin responses as opaque and cannot perform the verification.

Why SRI matters#

When you load a script from a CDN, you are giving that CDN the ability to execute arbitrary code on your page. The script runs with the same privileges as your own code. It can read cookies, access form data, make API calls on behalf of the user, and modify the DOM. If the CDN is compromised, or if an attacker can modify the file in transit, they gain all of those capabilities.

This is not a theoretical risk. In 2018, an attacker compromised the npm package event-stream by adding a dependency that contained code targeting a specific Bitcoin wallet application. The malicious code was distributed to thousands of projects before it was discovered. Similar supply chain attacks have targeted CDN hosted files, browser extensions, and package registries.

Supply chain attacks are increasing

Any external script without SRI is a potential entry point. Even trusted CDNs can be compromised through account takeovers, DNS hijacking, or server breaches. SRI ensures that a compromised server cannot deliver modified files to your users.

Without SRI, the only protection is trusting that every CDN you use will never serve a modified file. With SRI, you verify on every page load. The cost is minimal (a single hash comparison), and the protection is significant.

  • A compromised CDN cannot inject malicious code into your pages
  • Man in the middle attacks cannot modify scripts in transit (even without HTTPS on the CDN)
  • Accidental file corruption on the CDN is caught before it affects users
  • You maintain an auditable record of exactly which file versions your site depends on

How SRI works#

The process happens in four steps every time the browser encounters a tag with an integrity attribute.

  • The browser sends a CORS request to fetch the file from the external server
  • The server responds with the file contents and an Access-Control-Allow-Origin header
  • The browser computes the hash of the received file using the algorithm specified in the integrity attribute (sha256, sha384, or sha512)
  • The browser compares the computed hash against the expected hash. If they match, the file loads. If they differ, the browser blocks the resource and fires an error event

The hash algorithm determines the strength of the verification. SHA-256 is the minimum supported, SHA-384 is the most commonly used, and SHA-512 offers the strongest guarantee. If you include multiple hashes with different algorithms, the browser uses the strongest one. You can also include multiple hashes of the same algorithm to accept different versions of a file.

Multiple hash algorithms
<!-- Browser uses the strongest algorithm (sha384) -->
<script src="https://cdn.example.com/lib.js"
  integrity="sha256-abc123... sha384-def456..."
  crossorigin="anonymous"></script>

The crossorigin="anonymous" attribute is required for SRI to work on cross-origin resources. Without it, the browser fetches the file in "no-cors" mode, which means it cannot inspect the response body to compute a hash. The CDN must respond with Access-Control-Allow-Origin: * (or your specific origin) for the CORS request to succeed. Most major CDNs already include this header.

Generating SRI hashes#

Before you can add an integrity attribute, you need the hash of the file you want to protect. There are several ways to generate it.

Using the command line

Download the file first, then compute the hash. This approach lets you inspect the file before trusting it.

Linux / macOS
# Download the file
curl -o library.js https://cdn.example.com/library-3.6.0.min.js

# Generate SHA-384 hash
cat library.js | openssl dgst -sha384 -binary | openssl base64 -A

# Output: oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8w
Using shasum (alternative)
shasum -b -a 384 library.js | awk '{ print $1 }' | xxd -r -p | base64

Using Node.js

If you are already working in a Node.js environment, you can generate the hash programmatically.

Node.js
const crypto = require('crypto');
const fs = require('fs');

const fileBuffer = fs.readFileSync('library.js');
const hash = crypto.createHash('sha384').update(fileBuffer).digest('base64');
console.log('sha384-' + hash);

Using srihash.org

For quick one-off generation, you can paste a CDN URL into srihash.org. It fetches the file, generates the hash, and outputs a ready-to-use HTML tag. This is convenient for prototyping, but for production workflows you should generate hashes locally or in your build pipeline so you control the process end to end.

Implementing SRI in your HTML#

Adding SRI to an existing page is straightforward. For each external script or stylesheet, add the integrity and crossorigin attributes.

External scripts

HTML
<!-- Before: no protection -->
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

<!-- After: SRI protected -->
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"
  integrity="sha384-OYR4gnRmGfDMVlkHMDYnGZlKsAlAmFb8KIaifQ3gE4SW8zL2LCK2n3D0XBdiPFO"
  crossorigin="anonymous"></script>

External stylesheets

HTML
<link rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
  integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7icsOifkntC8ERbjxqLiBLQOsEmfAGBeLQT"
  crossorigin="anonymous">

Pairing SRI with Content Security Policy

SRI becomes even more effective when combined with Content Security Policy (CSP). You can use the require-sri-for directive to make the browser refuse to load any script or stylesheet that lacks an integrity attribute. This turns SRI from an opt-in check into an enforced requirement.

CSP header
Content-Security-Policy: require-sri-for script style

Browser support note

The require-sri-for directive was experimental and has been removed from most browsers. The recommended approach is to use SRI attributes directly on each tag and enforce their presence through your build process or linting rules rather than relying on this CSP directive.

Automating SRI in your build pipeline#

Manually generating and updating hashes works for a few files, but it becomes error-prone as your dependency list grows. Build tools can automate the process, generating fresh hashes every time you build and injecting them into your HTML.

Webpack

The webpack-subresource-integrity plugin automatically adds integrity attributes to all script and link tags generated by Webpack.

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

module.exports = {
  output: {
    crossOriginLoading: 'anonymous',
  },
  plugins: [
    new SubresourceIntegrityPlugin({
      hashFuncNames: ['sha384'],
    }),
  ],
};

Vite

For Vite projects, the vite-plugin-sri package handles SRI hash generation during the build step.

vite.config.js
import sri from 'vite-plugin-sri';

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

CI/CD verification

You can also add a step to your CI pipeline that checks for missing integrity attributes in your built HTML files. A simple grep or a custom script can scan for <script src= tags that lack an integrity attribute and fail the build if any are found.

Shell (CI step)
# Fail build if any external script lacks integrity attribute
if grep -rn '<script src="https' dist/ | grep -v 'integrity='; then
  echo "ERROR: External scripts found without SRI"
  exit 1
fi

Common pitfalls and how to avoid them#

Using unversioned CDN URLs

Some CDN URLs point to "latest" or omit a version number. When the library publishes an update, the file at that URL changes, and your SRI hash no longer matches. The browser blocks the file and your site breaks. Always pin to a specific version in the URL so the file content stays stable.

Version pinning
<!-- Bad: unversioned, file can change at any time -->
<script src="https://cdn.example.com/lib/latest/lib.min.js"
  integrity="sha384-..." crossorigin="anonymous"></script>

<!-- Good: pinned to exact version -->
<script src="https://cdn.example.com/lib@4.17.21/lib.min.js"
  integrity="sha384-..." crossorigin="anonymous"></script>

Forgetting the crossorigin attribute

Without crossorigin="anonymous", the browser makes a standard (non-CORS) request. The response is opaque, meaning the browser cannot read the bytes to compute a hash. The integrity check silently fails, and the resource is blocked. Always include both integrity and crossorigin together.

CDN does not support CORS

If the external server does not send an Access-Control-Allow-Origin header, the CORS request fails and the resource is blocked regardless of whether the hash matches. Before adding SRI, verify that the CDN supports CORS by checking its response headers. All major CDNs (jsDelivr, cdnjs, unpkg, Google Hosted Libraries) support CORS out of the box.

Dynamically loaded scripts

SRI only works on tags present in the HTML source. Scripts loaded dynamically through JavaScript (for example, tag managers that inject <script> elements at runtime) do not have integrity attributes unless you explicitly set them in your code. If you create script elements programmatically, set the integrity and crossOrigin properties before appending them to the DOM.

Dynamic script with SRI
const script = document.createElement('script');
script.src = 'https://cdn.example.com/analytics-2.0.js';
script.integrity = 'sha384-abc123def456...';
script.crossOrigin = 'anonymous';
document.head.appendChild(script);

Frequently asked questions#

What is Subresource Integrity (SRI)?

Subresource Integrity (SRI) is a browser security feature that lets you verify that files fetched from CDNs or other external servers have not been tampered with. You add a cryptographic hash to your script or link tag using the integrity attribute. When the browser downloads the file, it computes its own hash and compares the two. If they don't match, the browser blocks the file from executing.

How do I generate an SRI hash?

Download the exact file you want to protect, then run: openssl dgst -sha384 -binary file.js | openssl base64 -A. This outputs a base64 encoded SHA-384 hash. Add it to your HTML tag as integrity="sha384-{hash}" alongside crossorigin="anonymous". You can also use online tools like srihash.org to generate hashes from a URL.

Does SRI work with CSS stylesheets?

Yes. SRI works with both script tags and link tags for stylesheets. The syntax is identical: add an integrity attribute with the hash and a crossorigin attribute set to anonymous. Browsers will block the stylesheet from loading if the hash does not match, just like they do with scripts.

Why do I need the crossorigin attribute with SRI?

The crossorigin="anonymous" attribute tells the browser to make a CORS request without sending credentials. This is required for SRI because the browser needs access to the raw file bytes to compute the hash. Without CORS, the browser treats cross-origin responses as opaque and cannot verify the integrity hash. The CDN must also send the Access-Control-Allow-Origin header for this to work.

What happens when a CDN file changes and the SRI hash no longer matches?

The browser blocks the file from loading or executing. This is the intended behavior because it means the file has changed since you last verified it. You need to download the new version, verify that it is safe, generate a new hash, and update the integrity attribute in your HTML. This is why SRI works best with versioned CDN URLs that point to a specific, immutable file.

References

Was this helpful?
Share

Check your SRI coverage

Scan your website to see which external scripts and stylesheets are protected with integrity hashes and which are exposed.