cd /news/developer-tools/subresource-integrity-sri-pin-your-c… · home › topics › developer-tools › article
[ARTICLE · art-165494] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Subresource Integrity (SRI): Pin Your CDN Scripts

A developer behind browser-side security tooling at PixelValut published a hands-on guide to Subresource Integrity (SRI), showing how the browser hashes a downloaded script and blocks it if any byte differs from the integrity attribute. Testing in Chrome 156 via Playwright, the developer confirmed a correct SHA-384 hash with crossorigin="anonymous" loads dayjs 1.11.13 from jsDelivr, while a copy with one appended line was blocked with a digest-mismatch error. The writeup stresses that SRI requires CORS-enabled hosts and immutable, versioned files, and notes it would not have protected most sites caught in the 2024 polyfill.io incident.

by read6 min views1 publishedNov 6, 2026

Every <script src="https://cdn..."> on your site is a standing grant of full JavaScript execution to whoever controls that file tomorrow. Not the file you reviewed, the file the CDN serves on the next request. Subresource Integrity (SRI) is the one browser feature that turns that open grant into a fixed contract: run this exact file, byte for byte, or run nothing.

I run PixelValut, a set of browser-side developer and security tools, and SRI is one of the cheapest controls I know with real teeth. I wrote this with help from AI tools and checked every command and claim myself. All outputs below are from commands I ran today in a terminal and in Chrome 156 driven by Playwright.

Direct answer: SRI makes the browser hash a downloaded script or stylesheet and compare it with the integrity value in your HTML. If one byte differs, the browser blocks the file. It protects against a compromised CDN or a hijacked file. It needs crossorigin="anonymous" and a CORS-enabled host, and it only works for static, versioned files.

A third-party file can change without any change to your code: a CDN account gets phished, a package maintainer's token leaks, or a domain changes hands. The best-known case is polyfill.io in 2024. After the domain and project changed ownership, researchers reported it serving injected code to sites that still loaded it. SRI would not have saved most of those sites, for a reason I cover below, but it does stop the simpler and more common case: a pinned, static file that gets swapped.

I used dayjs 1.11.13 from jsDelivr as the example. First, the hash. SRI uses base64 of the raw digest, not the hex string most tools print:

curl -s -o dayjs.min.js https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js
openssl dgst -sha384 -binary dayjs.min.js | openssl base64 -A
DpVxUeeBWjUvUV1czyIHJAjh+jYUZFu2lLakbdua5vbwOrBGi1UgaKCHjTC+x3Ky

Cross-check it against an independent source. jsDelivr's data API publishes a SHA-256 for each file, and my local SHA-256 matched it exactly (nP25Pzivzy0Har7NZtMr/TODzfGWdlTrwmomYF2vQXM=). If you only hash the file you just downloaded, you are trusting that download.

Then the headers, because SRI on a cross-origin file only works if the host sends CORS headers:

HTTP/2 200
access-control-allow-origin: *
timing-allow-origin: *
cache-control: public, max-age=31536000, s-maxage=31536000, immutable
cross-origin-resource-policy: cross-origin
content-type: application/javascript; charset=utf-8

What each one tells you:

Header Value Why it matters for SRI
access-control-allow-origin * Lets the browser read the bytes in CORS mode, which it must do to hash them
cache-control immutable , 1 year A versioned URL that will not change, so the hash stays valid
content-type application/javascript Correct MIME type; with nosniff a wrong type is blocked anyway
cross-origin-resource-policy cross-origin The host allows embedding from other origins

The tag to ship:

<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js"
        integrity="sha384-DpVxUeeBWjUvUV1czyIHJAjh+jYUZFu2lLakbdua5vbwOrBGi1UgaKCHjTC+x3Ky"
        crossorigin="anonymous"></script>

I loaded three local test pages in Chrome and captured the console.

Run 1: correct hash, crossorigin="anonymous".

[log] dayjs loaded: true

Run 2: tampered file. I served a copy of the same file from a second local origin with CORS enabled, with one harmless line appended (console.log("injected line")). The integrity attribute still held the original hash:

[error] Failed to find a valid digest in the 'integrity' attribute for resource
'http://127.0.0.1:8766/dayjs.min.js' with computed SHA-384 integrity
'jBOi9x7Z1ktN8H4qfSwk+1UiIyzSbIfXvjjA8wEFLuW81GktpRPSoc9kdCMHsca6'.
The resource has been blocked.
[log] dayjs loaded: false

The appended line never ran. Note that the browser prints the hash it computed, which is handy when you are debugging a legitimate update.

Run 3: correct hash, but no crossorigin attribute.

[error] Subresource Integrity: The resource 'https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js'
has an integrity attribute, but the resource requires the request to be CORS enabled to check
the integrity, and it is not. The resource has been blocked because the integrity cannot be enforced.
[log] dayjs loaded: false

This is the most common SRI bug I see: a correct hash with a missing attribute. It fails closed, which is good for security and bad if nobody checked the console before deploying.

Resource type SRI works? Why What to do instead
Versioned library on a public CDN ( lib@1.2.3/file.js ) Yes Bytes never change for that URL Pin the version, add integrity andcrossorigin
"Latest" URL ( lib@3/file.js ,/latest/ ) Breaks on every update The bytes change while the hash does not Pin an exact version
Dynamic polyfill or user-agent-tailored bundles No Each browser may get different bytes Self-host a build you control
Tag managers, analytics and chat widgets Usually no Vendors update the in place Review vendors, restrict with CSP, load in isolation where possible
Your own hashed build assets ( app.3f9c1.js ) Yes Bundlers can emit hashes at build time Enable your bundler's SRI plugin, or rely on same-origin hosting
Web fonts and CSS from a CDN Yes, for <link> stylesheets Same rules as scripts Add integrity andcrossorigin to the<link>

The dynamic case is why SRI would not have rescued most polyfill.io users: a service that tailors output per browser cannot be pinned to one hash. The lesson is to stop code you cannot pin.

npm view dayjs@1.11.13 dist.integrity returns:

sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==

It looks like an SRI value, and it is in the same format, but it is the hash of the package tarball, not of dayjs.min.js. Pasting it into a script tag blocks the file. Hash the exact file at the exact URL you serve.

integrity and crossorigin.[...document.querySelectorAll('script[src],link[rel=stylesheet]')].filter(e => !e.integrity).map(e => e.src || e.href) lists every external tag without SRI.immutable caching, no "latest".openssl as above, or the <script> and stylesheet uses an exact version URLcrossorigin="anonymous" is present on every tag with access-control-allow-origin If you want the bigger picture on trusting files you did not build, see how to verify a download with SHA-256, GPG and Sigstore. It is the same idea at the file level.

Use sha384, the most common choice in CDN snippets, or sha512. Browsers also accept sha256. You can list several values in one attribute, separated by spaces, and the browser uses the strongest one it supports.

Either the URL is not pinned to an exact version, so the bytes changed, or the hash was generated from a different file. Pin the version and regenerate the hash from the URL you actually serve.

It is optional for same-origin files. The bigger risk is third-party hosts you do not control. Many bundlers can add SRI to their own output if you want defense in depth.

Hashing a typical library is cheap compared with down it. The real cost is operational: every upgrade needs a new hash, which is why it belongs in your build or CI.

Usually not, because vendors update those s in place, so a pinned hash breaks. Govern them with vendor review, a strict CSP and regular audits instead.

It makes the browser fetch the file in CORS mode without cookies. CORS mode lets the browser read the bytes and verify the hash. Without it, Chrome blocks a cross-origin file that has an integrity attribute.

── more in #developer-tools 4 stories · sorted by recency
── more on @pixelvalut 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/subresource-integrit…] indexed:0 read:6min 2026-11-06 · —