Skip to content
[menu][close]

FOUNDRYNETNo. 001SEPTEMBER 2026PROVENANCE

Subresource Integrity: Verify the Origin of Every Script

A script tag names a URL, and a URL is a promise about location, not content. Subresource integrity adds a digest to the tag so the browser can refuse any bytes that differ from what the page author hashed.

On this page

01 What subresource integrity is

Subresource integrity is a W3C mechanism that pins a fetched file to a digest chosen by the page author, carried in an integrity attribute on a script or link element: an algorithm token, a dash and a base64 digest, for example sha384-OqF1oQE0.... The browser fetches the file, hashes the body and compares. On a match it executes or applies the file. On a mismatch it discards it and raises a network error.

The specification became a W3C Recommendation on 23 June 2016. It permits SHA-256, SHA-384 and SHA-512, the SHA-2 functions of FIPS 180, republished with reference code in RFC 6234. When an attribute lists several digests, the browser applies only the strongest algorithm it supports. A cross-origin file also needs crossorigin="anonymous" and a CORS header from the file's server, because the specification will not report pass or fail on a response the page may not read; otherwise a page could confirm guesses about private resources one digest at a time. This page sits in the lane on proving what the wire delivered. Digests on any HTTP response are covered in Content-Digest headers and end-to-end hashing; SRI is the case where the browser enforces the check itself.

02 What SRI proves and what it does not

The claim is narrow and strong. If the browser executes the file, the bytes it executed hash to the value in the page. A CDN edge, a proxy, a shared cache or a compromised vendor account can alter the file, and the browser will refuse it. The specification's stated threat is a compromised third-party host: including a library from a vendor's CDN should not mean losing your site when the vendor does.

The claim stops at the page that carries the attribute. The digest lives in HTML served by the first-party origin; anyone who can change that HTML can change the digest to match a substituted script. SRI does not defend against a compromised first party or a stolen deploy credential. Nor does it judge behaviour: a hostile script with a correct digest passes.

SRI is the only mechanism a browser enforces per file against an author-chosen value.
MechanismWhat it provesTrust anchorWhat it does not cover
TLSThe bytes came unaltered from the server holding the keyPublic CA plus the server's keyA modified file on the server's disk
Subresource integrityThe bytes executed equal the bytes the author hashedThe HTML document carrying the digestA compromised first-party origin; the code's safety
Content Security PolicyThe file came from a permitted host, or matches a hash or nonceA first-party response headerFile content, with host allow-lists alone
Code signingA named signer vouched for these bytes at a point in timeThe signer's key and chainNot applied by browsers to script includes

03 SRI and Content Security Policy

Early drafts of CSP Level 3 and SRI Level 2 carried a require-sri-for script style directive that would block any script or stylesheet lacking an integrity attribute. It shipped only behind flags and was dropped from both. The SRI Level 2 Working Draft of 20 March 2026, served at the W3C's /TR/sri/ address, replaces it with an Integrity-Policy response header, blocked-destinations=(script), a report-only variant and an integrity-violation report type. Treat that as a draft. Today, CSP says which hosts may serve scripts; SRI says which bytes may run. Use both.

04 Generating the digest

The MDN Subresource Integrity page and the specification give the same OpenSSL pipeline: hash in binary mode, then base64 on one line. The transcript hashes a small file, builds the tag and shows a gzip-compressed copy yielding the same digest.

Digest, tag and the effect of gzip
$ cat widget.js | openssl dgst -sha384 -binary | openssl base64 -A
OqF1oQE0GaKZnybVscEipEzSxeXSSOzxiuvuDO5llaggN6dikaYPW5rWx5ByruYA
$ gzip -dc widget.js.gz | openssl dgst -sha384 -binary | openssl base64 -A
OqF1oQE0GaKZnybVscEipEzSxeXSSOzxiuvuDO5llaggN6dikaYPW5rWx5ByruYA
<script src="https://static.example.net/widget.js"
        integrity="sha384-OqF1oQE0GaKZnybVscEipEzSxeXSSOzxiuvuDO5llaggN6dikaYPW5rWx5ByruYA"
        crossorigin="anonymous"></script>

Section 3.3.1 of the 2016 Recommendation applies the algorithm to the representation data without any content-codings. Content-Encoding: gzip or br is therefore harmless. Anything that changes the decoded bytes is not.

05 Subresource integrity through a balancer or CDN

The edge is where SRI most often breaks, always because a device rewrote the body. A layer 4 balancer forwards TCP segments and cannot touch content; a layer 7 device can. The distinction in which balancer layer can rewrite a response body is the first check when a pinned script fails after a network change.

Edge features that alter decoded bytes include automatic minification, bundling, injected analytics or consent snippets, and URL rewriting inside the file. Each means a blocked load. The specification's proxies section says an intermediary that modifies responses must keep the digest in sync or deliver the canonical version, and that servers should send Cache-Control: no-transform. Caching is fine: a cache returns the bytes it stored, and long cache lifetimes for static script assets pair well with SRI, since a pinned file is immutable and can carry a long max-age.

Where a digest can change on the way to the browserWHERE A DIGEST CAN CHANGE ON THE WAY TO THE BROWSER01Build emitsfile, digestcomputed02Origin servesfile, gzipapplied03Layer 7 edgemay minify orinject04Browserdecodes,hashes body05Matchexecutes;mismatchblocksWhere a digest can change on the way to the browserWHERE A DIGEST CAN CHANGE ON THE WAY TOTHE BROWSER01Build emits file, digest computed02Origin serves file, gzip applied03Layer 7 edge may minify or inject04Browser decodes, hashes body05Match executes; mismatch blocks
Compression at step two survives the check; any rewrite at step three does not.

06 Worked example: one pinned script, one edge feature

A site at www.example.com includes a vendor library from static.example.net, served with Access-Control-Allow-Origin: *. The operator pins it with the tag from the transcript; the file is 65 bytes and runs. A month later the vendor's host is compromised. The altered library has a different digest, every browser refuses it, and the site loses one feature for a day instead of serving the attacker's code.

Separately, the operator moves first-party scripts behind a CDN with automatic minification on. The pinned widget.js is now served as window.fnWidget=function(e){e.textContent="ready"};, 51 bytes, which hashes to sha384-lsoV4ezG.... The attribute still says OqF1oQE0.... Every browser blocks it until minification is disabled for that path or the digest is regenerated from the minified output. Nothing malicious happened; the digest did its job.

07 Rollout procedure on a site behind a CDN

  1. Inventory the includes

    List every script and stylesheet link with its origin. Third-party hosts come first.

  2. Confirm CORS on each host

    Fetch each file with curl -I and look for Access-Control-Allow-Origin. A host that omits it cannot be pinned cross-origin.

  3. Turn off body transformation for pinned paths

    Disable minification, bundling and snippet injection at the CDN and any layer 7 balancer for those paths; set Cache-Control: no-transform at the origin.

  4. Generate digests from the served bytes

    Download each file through the CDN, not from the build directory, and run the OpenSSL pipeline on it. Hashing the local copy pins a digest the edge may never match.

  5. Add the attributes and test

    Add integrity and crossorigin="anonymous", then load each page with the console open. A blocked resource shows as a failed integrity check with its URL.

  6. Monitor and version deliberately

    Beacon onerror events from pinned elements to a collector, review CSP reports for scripts from unexpected hosts, and update the digest with every release of the file.

08 Pitfalls: mutable URLs, missing CORS and silent edges

  • Pinning a URL that changes underneath you. A path like /lib/latest.js is updated in place. Pin only versioned paths such as /lib/2.4.1/lib.js.
  • Forgetting crossorigin. An integrity attribute on a cross-origin element without crossorigin="anonymous" is a guaranteed block. The browser will not run the check on a response it cannot read.
  • Hashing the build artifact, serving something else. A stripped source map comment, a trailing newline or a byte order mark all change the digest. Hash what the edge returns.
  • Relying on SRI for first-party protection. If the HTML can be changed, so can the digest. SRI defends against a compromised third party or a rewriting intermediary, not against a compromised origin.

SRI is the browser-side member of a family: comparing a published SHA-256 digest before you install a download is the same idea for files an operator fetches, and a signature that names the publisher as well as fixing the bytes is the step SRI deliberately leaves out.

09 Questions

What is subresource integrity in one sentence?

A browser check in which a script or link element carries a digest of the expected file, and the browser refuses to execute or apply a fetched file whose SHA-256, SHA-384 or SHA-512 hash does not match, so a modified copy from any CDN, proxy or cache is blocked.

Which hash algorithm should I use?

SHA-384 is the common choice and the one the specification and MDN use in their examples. All three are SHA-2 functions from RFC 6234, supported by every current browser. Since the browser applies only the strongest listed algorithm, two SHA-384 values for two versions of a file are more useful than mixed algorithms.

Does gzip or Brotli compression break SRI?

No. The 2016 Recommendation applies the hash to the representation without content-codings, so the browser decodes the body before hashing. A compressed transfer of the same file gives the same digest. Only a change to the decoded bytes, such as minification or an injected snippet, changes the result.

Why does my CDN break my integrity attributes?

A feature on the edge is rewriting the file body. Automatic minification, bundling, URL rewriting and script injection all produce bytes different from the ones you hashed. Disable those features for pinned paths, send Cache-Control: no-transform, and generate digests from the file as fetched through the CDN.

Does SRI replace Content Security Policy?

No. CSP restricts where scripts may come from and can require nonces or hashes on inline code. SRI checks an external file's content against a digest in the page. The require-sri-for directive that tied them together was dropped; the draft replacement is an Integrity-Policy header in SRI Level 2. Use both together.

Can SRI protect a site whose own server is compromised?

No. The digest is carried in the HTML, and whoever controls the origin controls both the script and its digest. SRI protects against a compromised third-party host and intermediaries that alter bytes. Protecting the first-party origin is a matter of deploy credentials, change control and monitoring.

Subresource integrity is one attribute and one hash, and it turns a script include from a location into a commitment about content. Pin versioned third-party files first, keep the edge from rewriting anything pinned, hash what the CDN serves, and watch for blocked loads so the first failure is a report and not an outage.