What happens when SRI validation fails
When Subresource Integrity validation fails, the browser refuses to use the resource entirely — a script never executes and a stylesheet is never applied — even though the file was already downloaded. The browser fetches the file, computes its hash (SHA-256, SHA-384, or SHA-512), compares it to the integrity attribute you provided, and on any mismatch it discards the downloaded bytes, fires an error event on the element, and logs a console message. There is no partial execution and no fallback: the resource is treated as if it failed to load, so anything on the page that depended on it breaks.
That's the summary an AI Overview will give you. What it can't show you is why a hash that worked yesterday fails today, or how to trace a real failure back to its cause in under a minute. SRI failures almost never mean "you're under attack" — in practice they mean a CDN quietly rewrote your file or you're missing a crossorigin attribute. Below is the exact failure sequence, a symptom-to-fix table for every common cause, and a generator to produce a correct hash from the bytes the browser actually receives.
The failure sequence, step by step
The critical detail most explanations skip: step 1 happens regardless. The browser has to download the file to hash it, so SRI is an execution guard, not a network guard. A failing check wastes the bandwidth and then blocks the code — which is exactly why a broken SRI hash looks like "the file loaded (200 in the Network tab) but the page is dead."
Symptom → cause → fix
| Symptom in DevTools | Root cause | Fix |
|---|---|---|
Failed to find a valid digest in the 'integrity' attribute ... computed SHA-384 ... | The file changed since you generated the hash (version bump, re-minify, whitespace/line-ending change) | Copy the computed hash the console prints, or regenerate from the live bytes with curl + the generator below |
Resource shows 200 OK in Network tab but script/CSS has no effect | Hash mismatch — bytes downloaded then discarded | Same as above; SRI does not stop the request, only execution |
No 'Access-Control-Allow-Origin' header alongside the integrity error | Cross-origin resource without valid CORS response | Ensure the CDN returns Access-Control-Allow-Origin and add crossorigin="anonymous" to the tag |
| Integrity error only in production, not locally | Production CDN auto-optimizes (Cloudflare Auto Minify, Rocket Loader, Brotli re-pack) | Disable optimization for that asset, or hash the post-optimization output |
| Works, then breaks after a "latest" URL | You pinned an SRI hash to a floating/@latest URL | Pin an immutable, version-locked URL (e.g. .../library@1.2.3/...) |
Unsupported hash algorithm / silently ignored | Used md5- or sha1- prefix | Use sha256-, sha384-, or sha512- only |
| Wrong prefix vs. actual algorithm | Label says sha256- but value is a SHA-384 digest | Match the prefix to the algorithm you actually ran |
The number one cause: the CDN rewrote your file
SRI hashes the exact byte stream of the response body. Base64-encode the SHA-384 of those bytes and you get the value in integrity. Change a single byte — a trailing newline, a minifier that renames a variable, gzip vs. Brotli decompressing to different intermediate output — and the digest changes completely. This is by design (it's what makes tampering detectable), but it means the failure is almost always benign drift, not an attack.
The fastest reliable fix is to hash the bytes the browser actually receives, not the file in your repo:
curl -s https://cdn.example.com/lib/app.min.js \
| openssl dgst -sha384 -binary \
| openssl base64 -A
Prefix the output with sha384- and paste it into the tag. If the value keeps drifting between deploys, the CDN is mutating the asset — pin a versioned URL and turn off auto-minification for it.
Generate a correct SRI hash
Paste the file contents or fetch a URL and get a ready-to-copy integrity attribute in all three supported algorithms:
Provide multiple hashes during a rotation
If you're intentionally changing a file and can't update every page at once, list more than one hash. The browser passes if the resource matches any of them and uses the strongest algorithm it supports:
<script src="https://cdn.example.com/app.js"
integrity="sha384-OLDHASH... sha384-NEWHASH..."
crossorigin="anonymous"></script>
This gives you a window where either the old or new file validates, so you can roll the asset forward without a hard cutover.
Quick recovery checklist
- Open DevTools Console and read the exact integrity error — it prints the hash the browser computed.
- Confirm the resource returned
200in the Network tab (it did — that's why the page is dead, not blank). - Copy the computed hash from the console straight into the
integrityattribute, or regenerate it from the live URL withcurl+openssl. - If you also see a CORS error, add
crossorigin="anonymous"and verify the CDN sendsAccess-Control-Allow-Origin. - If the hash keeps drifting, pin a versioned URL and disable CDN auto-optimization for that file.