Subresource Integrity (SRI) in 2026
Subresource Integrity is a one-line W3C feature: stick an integrity="sha384-…"
attribute on your <script> or <link> tag, the browser
fetches the resource and refuses to execute it if the body's hash doesn't match. Compromise
at the CDN gets neutralised at the parsing step. CDN-served jQuery without SRI is a single
supply chain attack away from being your XSS.
The threat model
You include https://cdn.example.org/lib.js on every page. The CDN gets
compromised. Attacker swaps lib.js for a malicious version — every visitor
to your site executes it as same-origin. CSP doesn't help because the source matches the
whitelist.
SRI fixes this in one line:
<script
src="https://cdn.example.org/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
Browser fetches the file, computes SHA-384, compares. Mismatch → script is rejected, no execution. Even a one-byte modification triggers the rejection.
When it actually helps
- Third-party CDN with no version pinning (
cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js). SRI prevents an upstream compromise from reaching your users. - Vendor-distributed libraries from a CDN you don't control — Stripe.js, Google Fonts (kind of), Recaptcha.
- Long-lived embeds on landing pages or marketing sites that don't get re-deployed when libs are updated.
When it's theatre
- Same-origin scripts from your own server. If your origin is compromised, SRI is useless because you're computing the integrity hash on the same compromised host.
- Dynamically generated scripts. SSR-emitted JS body changes per request — SRI assumes byte-identical content.
- SPAs that bundle everything. Your webpack/Vite output is hashed at build time and lives at
/_app/immutable/start.7jP7wo7n.js. The hash is in the URL — same protection, different mechanism.
How to compute the hash
$ curl -s https://cdn.example.org/lib.js | openssl dgst -sha384 -binary | base64
oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC
# Then prefix with the algo name
sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC
Most CDNs (jsdelivr, cdnjs, unpkg) display the SRI hash on their UI alongside the URL. Just copy.
The crossorigin attribute
SRI requires the cross-origin response to include
Access-Control-Allow-Origin headers, which means the request must use CORS,
which means you need crossorigin="anonymous" (or "use-credentials").
Without it, the browser silently degrades to "no integrity check" — your tag passes
validation but doesn't actually verify.
Always include both integrity AND crossorigin. Always.
Common mistakes
- Forgetting
crossorigin. SRI silently disabled. Test with the resource modified deliberately to confirm rejection works. - Hash mismatch on CDN auto-minification. Some CDNs change the file (whitespace, etc.). Always pin to a specific version URL.
- Wrong algo. SHA-256 is allowed but SHA-384 is recommended. SHA-1 is rejected by all browsers.
- Including the hash in HTML, but the resource updates. When you update the lib, you have to update the hash. Pinning + automation prevents this.
How UnveilScan checks SRI
Our mixed_content checker (Extended profile) parses HTML for cross-origin
<script> and <link rel=stylesheet> tags, reports
any without an integrity attribute as LOW. Same-origin tags are not
flagged (they don't benefit from SRI in the threat model).
Coverage is reported as a percentage: "13/15 cross-origin scripts have integrity (87%)" — so you can see at a glance whether your CSP + SRI deployment is meaningful.
Audit your SRI coverage
Extended scan parses every cross-origin script + stylesheet and reports SRI completeness.
See pricing