What Is Subresource Integrity (SRI)?

Plus hash generation, the crossorigin gotcha that fails silently, how SRI relates to CSP, and two real incidents that show exactly where it helps — and where its design runs out.

Subresource Integrity (SRI) is a browser security feature that lets a website tell the browser exactly what a third-party file — a script from a CDN, a stylesheet, a font — is supposed to contain, using a cryptographic hash. If the file the browser actually receives doesn't match that hash, the browser refuses to run it. It's a W3C standard supported in every major browser, and it exists specifically to stop a compromised CDN or a hijacked third-party script from running attacker-controlled code on your visitors' machines.

This guide covers how SRI actually works, how to generate the hashes, the crossorigin requirement that causes most real-world SRI setups to silently fail, two real incidents that show exactly where it helps and where it doesn't, its genuine limitations, how it relates to Content Security Policy, and how to implement it across a static site, a build pipeline, or WordPress.

How SRI Works

SRI works by adding an integrity attribute to a <script> or <link> tag, containing a hash of the exact file you expect the browser to receive:

<script src="https://cdn.example.com/library.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"></script>

When the browser fetches that file, it computes its own hash of exactly what it downloaded and compares it to the one in the integrity attribute. A match means the file runs normally. A mismatch — because the CDN was compromised, someone on the same public Wi-Fi altered the file in transit, or the CDN simply served the wrong version — means the browser discards the file, and it never executes at all. Nothing loads instead of something malicious loading; a broken feature is the intended failure mode.

Generating an SRI Hash

You have three practical ways to produce the hash value that goes in the integrity attribute.

Command line, using OpenSSL — the most direct method, and the one that doesn't depend on any third-party service:

openssl dgst -sha384 -binary library.js | openssl base64 -A

That outputs the raw hash string; prefix it with sha384- and drop it into the integrity attribute.

A hash generator website, like the community-run srihash.org — paste in the CDN URL, get back a ready-to-use script tag. Convenient for a one-off addition, but it means trusting that site to fetch the correct file at the moment you use it.

Build-tool automation — for anything beyond a handful of static includes, generate the hash automatically at build time (webpack and Vite both have plugins for this, and most static site generators support a pre-deploy hook) so the hash updates itself every time the dependency version changes, rather than depending on a developer remembering to regenerate it by hand.

Choosing a Hash Algorithm

Use SHA-384 unless you have a specific reason not to — it's what OWASP's own examples use, and it's the algorithm most CDNs auto-generate when they publish integrity hashes alongside their script tags. SHA-256 and SHA-512 are also valid and browser-supported; per MDN's SRI reference, a browser given several hashes picks the strongest algorithm present, so the practical difference between the three is hash length and computation cost, not security in any way that matters for this use case.

SHA-1 is not a valid choice. It's cryptographically broken — "collision-resistant" no longer describes it accurately — and browsers exclude it from SRI's supported algorithm list for exactly that reason.

The integrity attribute also accepts multiple space-separated hashes for the same file, letting the browser accept whichever one it supports first. That's algorithm agility, not redundancy for its own sake — it lets a site add a newer algorithm without breaking older browsers still checking against the old one.

The Crossorigin Requirement

Cross-origin resources — anything loaded from a different domain than your page — are fetched in a restricted mode by default that doesn't let the browser read the response body at all, which means it can't compute a hash to check against your integrity attribute. Adding crossorigin="anonymous" switches that fetch to use CORS instead, without sending cookies or credentials, which gives the browser read access to the actual bytes it needs to verify.

Here's the part that catches almost everyone at least once: leaving crossorigin off doesn't produce an error. The script keeps loading exactly as before, integrity attribute and all, but the browser silently skips the verification entirely — it fails open, not closed. A developer who adds integrity without crossorigin, tests the page, sees it working, and walks away has shipped a page with zero actual protection while believing otherwise.

Worth knowing: a working page tells you nothing about whether SRI is actually active. To check, open DevTools' Network tab, inspect the resource's response headers for Access-Control-Allow-Origin, and confirm crossorigin is actually present in the rendered HTML — not just in the source file you meant to ship.

Real-World SRI Incidents

Two incidents, six years apart, show exactly where SRI helps and exactly where its design runs out.

In February 2018, security researcher Scott Helme discovered that BrowseAloud, a widely used accessibility script from UK company Texthelp, had been quietly modified to inject the Coinhive cryptocurrency miner into every page that loaded it. More than 4,000 websites were affected, including the UK's Information Commissioner's Office, the NHS, and the U.S. federal courts system — anyone visiting one of those pages had their CPU silently hijacked to mine Monero for as long as the tab stayed open. Texthelp pulled the script within hours, but the incident became the textbook argument for SRI, because Helme said so directly at the time: BrowseAloud was a static file, loaded from a single, predictable URL, that changed only when its owner intentionally updated it. A one-time hash, checked on every load, would have caught the tampering the instant it happened and simply refused to run the altered file.

Polyfill.io shows the opposite case. In February 2024, the popular polyfill.io service — used by more than 100,000 sites to add missing JavaScript features for older browsers — was sold to a Chinese company, Funnull. By June 2024, security firm Sansec confirmed the service was injecting code that redirected mobile visitors to betting sites, using obfuscated logic designed to evade detection by site administrators. Cloudflare, Google, and Fastly all stepped in with safe mirrors, and Namecheap suspended the domain — and in March 2026, Hudson Rock published evidence tying the control panels behind it to a North Korean operator working through the Chinese shell company originally blamed.

Here's the catch: polyfill.io couldn't have been protected by SRI even if every site using it had tried. The entire service works by reading each visitor's browser type from the request and returning different JavaScript to different browsers — that's the actual feature it was built to provide. Because the response is never the same file twice, there's no single hash a site could pin, which means SRI's core mechanism — verify this exact resource matches this exact hash — cannot apply to a resource whose whole design is not being an exact, fixed thing.

SRI Limitations

SRI and Content Security Policy

SRI and Content Security Policy (CSP) solve two different problems and work best together, not as substitutes for each other. CSP controls which sources a page is allowed to load scripts and styles from at all; SRI checks whether what actually arrived from an already-approved source matches what you expected.

An earlier CSP directive, require-sri-for, was designed to make integrity attributes mandatory — a page could declare that it would refuse to load any script lacking one. It's since been removed from most browsers as an experimental feature that never reached broad enough adoption to standardize. The current recommended alternative is a newer mechanism, the Integrity-Policy header, which lets a page require that its external scripts load with integrity metadata present, with a report-only mode available first to surface violations before enforcing them.

In practice: enforce which domains you trust with CSP, then verify what those domains actually deliver with SRI. The same CSP layer that defends against session hijacking is doing a related but distinct job here — neither one substitutes for the other.

Common SRI Mistakes

Implementing SRI

Static Sites

Add integrity and crossorigin by hand to each script and link tag, generating each hash with the OpenSSL command above. This is fine for a handful of includes and doesn't scale much past that — a site pulling in a dozen CDN resources will start missing updates.

Build Tools

Webpack and Vite both have plugins that generate and inject SRI hashes automatically at build time, so the hash regenerates itself every time a dependency version changes rather than relying on a developer to remember. For anything without a bundler, a small pre-deploy script running the OpenSSL command across your dependency list and writing the results into your templates accomplishes the same thing.

WordPress

WordPress doesn't add SRI to enqueued scripts by default, but its script_loader_tag filter lets a theme or a small plugin add integrity and crossorigin attributes to any script or stylesheet WordPress outputs, without editing core files directly.

Software Supply Chain Attacks

SRI protects one specific link in a much longer chain: the moment a browser fetches a third-party file. A padlock in the address bar does not help either — HTTPS proves the file arrived untampered from the CDN, not that the CDN sent something safe. SRI says nothing about how that file got compromised in the first place, or about attacks that happen further upstream — a poisoned npm package, a hijacked build pipeline, a compromised maintainer account — none of which involve a CDN request SRI could ever see. Treat SRI as one control among several: pin your dependency versions, review what a package actually does before adding it, and monitor for unexpected changes in libraries you don't control, rather than treating a hash in a script tag as supply chain security solved.

Does SRI work with every CDN?

Only if the CDN supports CORS — meaning it sends the Access-Control-Allow-Origin header needed for crossorigin="anonymous" to work. Nearly every major public CDN (cdnjs, jsDelivr, Google Hosted Libraries) does. What SRI cannot work with, regardless of CORS support, is a CDN that intentionally serves different content per request, like a dynamic polyfill or personalization service — there is no single fixed hash to pin in that case.

Will SRI break my site when a CDN updates a file?

Yes, and that is the point. If a library changes at the same URL you have pinned, the browser computes a new hash, sees it does not match your integrity attribute, and blocks the file rather than silently running a version you never approved. The fix is to update the hash at the same time you update the library version — pin both together, not just one.

Can SRI protect against a man-in-the-middle attack?

Partially. If an attacker intercepts the connection and alters a script in transit, SRI catches the mismatch and blocks the file, regardless of how the tampering happened. It will not stop every form of interception, though — the risks around public Wi-Fi cover the interception techniques SRI has no visibility into.

Does adding SRI slow down page load?

No, not in any way a visitor would notice. Hash verification happens natively in the browser using the same engine that already handles HTTPS, and it adds no extra network round trip — the browser is already downloading the file, it is just also checking the bytes it received against the hash you provided.

Is SRI required by any compliance standard?

Not by law, but it shows up in industry standards. PCI DSS 4.0.1, Requirement 6.4.3, requires integrity checks on scripts running on payment pages, and SRI is one of the accepted ways to satisfy that requirement. Security-rating services also flag missing SRI on third-party resources as a factor in public breach-risk scoring.

Can I use SRI with Google Fonts or analytics scripts?

Rarely, in practice. Most analytics and font-loading scripts are served from URLs their vendor updates without warning and without a version-pinned path, which means the content — and therefore the hash — can change at any time outside your control. SRI works cleanly on a library pinned to a specific version number in the URL; it works poorly on anything designed to update itself silently.

What does a visitor actually see if an SRI check fails?

Nothing resembling a warning. There is no interstitial or browser prompt the way there is for an SSL certificate error — the script or stylesheet simply does not load, and the console shows a message like "Failed to find a valid digest" that only someone with DevTools open would notice. To the visitor, it just looks like a broken feature.


Community

Get the next leak test before it's news

Tool releases and research notes, sent when there's something worth reading. Nothing else.

At least 10 characters.