Subresource Integrity: when it helps and when it does not
Published May 27, 2026. About a five-minute read.
Subresource Integrity (SRI) lets you tell the browser the exact hash of a JavaScript or CSS file you are loading from an external server. If the file the server returns does not match the hash, the browser refuses to execute it. The attribute looks like this:
<script
src="https://cdn.example.com/lib/foo-2.1.0.min.js"
integrity="sha384-Q5xV5xS6...long base64...vXJq=="
crossorigin="anonymous"
></script>On paper, SRI sounds like a silver bullet against compromised CDNs. The reality is narrower. SRI is great at one specific job and useless at several other jobs people assume it does.
What SRI actually protects against
Tampering with a specific, immutable resource on a third-party server. If you load a pinned version of a library (jquery-3.7.1.min.js) from a CDN, and the attacker swaps that file with a backdoored one, SRI catches it. The hash will not match. The browser refuses to run the modified script.
This is a real protection. CDN compromises happen.
What SRI does not protect against
Anything dynamic. If the file you are loading changes per request (analytics scripts, A/B tests, chat widgets, advertising libraries), SRI cannot work, because there is no stable hash. You would have to regenerate the hash every time the vendor updates the file, which they do without telling you.
Compromised same-origin scripts. If the attacker can modify the script on your server, they can also modify the HTML that references it (and the integrity attribute). SRI is for third-party content.
The first time a malicious version is deployed by the legitimate vendor. If the vendor themselves ships a backdoored update, you will dutifully update the integrity hash to match, because the new file is what they officially shipped. SRI catches network and CDN tampering, not supply-chain compromise of the source.
Anything loaded via JavaScript at runtime. SRI applies to <script> and <link rel="stylesheet"> tags in the HTML. Scripts injected by JavaScript with document.createElement or fetched via import() are out of scope.
When to use it
SRI is worth setting up in two specific cases:
- You include a pinned version of an open-source library from a public CDN (jsDelivr, cdnjs, unpkg). The file at the version-pinned URL should never change. SRI catches the case where it does.
- You include a script or stylesheet from a trusted vendor whose deploys are infrequent and version-pinned. Each version-bump is a controlled event where you can update the integrity hash deliberately.
When not to use it
- Analytics, A/B testing, ads, customer-support widgets, tag managers. These change too often. Attempting SRI breaks the site every time the vendor pushes an update.
- Same-origin scripts. The attacker who can edit the file can also edit the hash.
- Anything loaded dynamically by other scripts.
How to generate the hash
The hash is a SHA-256, SHA-384, or SHA-512 of the file contents, base64-encoded. Generate it once when you pin the version:
curl -sL https://cdn.example.com/lib/foo-2.1.0.min.js |
openssl dgst -sha384 -binary |
openssl base64 -AMost CDNs (jsDelivr, cdnjs) publish the SRI hash directly on their copy-paste snippet for each version, so you usually do not need to generate it yourself.
The crossorigin="anonymous" attribute is required when you load an SRI-protected resource from a different origin. Without it, the browser cannot validate the response and the script will not load.
SRI is one piece, not the whole defense
The more general protection against third-party script tampering is CSP. With a tight script-src list that names specific domains and (optionally) strict-dynamic with nonces, you limit which scripts can run at all. SRI is a per-script integrity check on top of that. They compose well, but neither is a substitute for the other.
Two short rules of thumb:
- If you are loading a pinned-version library from a public CDN, add SRI. It is essentially free.
- If you are loading a dynamic third-party script, do not add SRI. Lock down the source via CSP instead and accept the residual risk.