Subresource Integrity
Date: 2026-08-17
Pinning a third-party file’s exact contents with a hash, so the browser refuses to execute it if the bytes change. It defends against a compromised CDN — and against the far more common case of a vendor silently shipping a new version of a file at the same URL.
Subresource integrity (SRI) is an integrity attribute on a <script> or <link> holding a hash of the expected file, which the browser checks before using the file.
The mechanism
<script src="https://cdn.example.com/lib@3.2.1/lib.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>1 browser downloads the file
2 computes SHA-384 of the exact bytes received
3 compares against the integrity attribute
4 match → execute
MISMATCH → refuse. the script never runs
Byte-exact. A single changed character — a whitespace difference, a rebuild with a different minifier — produces a different hash and blocks the file.
crossorigin="anonymous" is mandatory. Without it the browser can’t read the response body for a cross-origin resource, so it can’t verify the hash, and it blocks the resource outright. The attribute also means no cookies are sent with the request, which for a CDN asset is what you want anyway — CORS.
Generating the hash
curl -s https://cdn.example.com/lib@3.2.1/lib.min.js \
| openssl dgst -sha384 -binary \
| openssl base64 -APrefix the result with sha384-. SHA-384 is the usual choice; SHA-256 and SHA-512 are also accepted. Multiple hashes can be supplied space-separated, and the browser accepts the file if any one matches — useful during a planned rotation.
What it defends against
✓ CDN COMPROMISED an attacker replaces the file at its host
→ hash mismatch → blocked
✓ SILENT VERSION CHANGE the vendor rebuilds and republishes at the
same URL, changing behaviour under you
→ blocked, and you find out immediately
← this is the common one
✓ MITM ON A WEAK PATH modified in transit
→ blocked (though HTTPS already covers this)
✗ A MALICIOUS FILE YOU PINNED
SRI proves the bytes are UNCHANGED.
it says nothing about whether they were
ever safe
✗ ANYTHING THE SCRIPT LOADS ITSELF
a pinned script that fetches and evals more
code is unprotected past the first file
← this is why SRI is near-useless for tag
managers and most analytics vendors
The last one is the real limit. A tag manager’s container script is a loader; its whole purpose is to fetch code decided elsewhere at runtime. Pinning it protects one file and nothing that file goes on to run — Tag Manager Performance, Third-Party Scripts.
The operational problem
SRI and auto-updating are incompatible by design.
<script src="https://cdn.example.com/lib@3/lib.js" integrity="sha384-…">
↑
a floating major version. the vendor ships
3.2.2, the hash breaks, YOUR SITE BREAKS —
and it breaks at the moment they deploy,
not at the moment you do
So SRI requires exact version pinning, and exact version pinning means you own the upgrade — including security patches. That’s a real trade and worth stating: you’ve swapped “silent changes I didn’t authorise” for “changes I must actively apply”.
Fail-open is not available. There’s no “warn but run” mode, which is correct security design and means every SRI failure is a broken feature.
The alternative that’s usually better
Self-host the file.
THIRD-PARTY CDN + SRI SELF-HOSTED
pinned to one version pinned by your own build
breaks if they change it can't change without your deploy
one more origin to connect to no extra connection
one more party to trust one fewer
no shared-cache benefit no shared-cache benefit
(storage is partitioned) — identical
Since the HTTP cache is partitioned, the historic performance argument for a public CDN is gone. Self-hosting gives you everything SRI does, plus fewer origins, without the breakage risk — so for a library you control the choice of, it’s the better answer.
SRI’s remaining place is where self-hosting isn’t possible: a widget that must be served from the vendor’s domain, or a script whose licence requires it.
Practical
- Generate hashes in the build, not by hand. A manual hash goes stale and nobody notices until it breaks
- Monitor for SRI failures. They surface as a console error and a missing feature, not as an exception your error tracker catches by default. Wire up a listener, or a Content Security Policy report — Error Tracking, Content Security Policy
- Have a rollback path. If a pinned script starts failing, you need to ship a new hash quickly — Rollback and Forward Fix
- Never use SRI on a URL that returns different content per request — a personalised endpoint, or anything with a session in it
require-sri-forin CSP can enforce that scripts and styles carry integrity attributes, which stops one being added without one
Where it interacts
- Content Security Policy — the complementary control: CSP restricts where code may come from, SRI pins what the code is
- Supply Chain Risk — the threat model, and the build-time equivalents (lockfiles, provenance) that cover far more of it
- Storage Partitioning — why the public-CDN argument SRI existed to make safe has largely evaporated
- Dependency Management — exact pinning is the same discipline applied at a different layer