Subresource Integrity (SRI): How to Hash Your Assets and Stop Supply-Chain Surprises
Third-party scripts are a trust fall. SRI lets you pin exact bytes with a hash so a swapped or tampered file won’t execute. Here’s how I generate hashes, wire them into tags and CSP, and automate the whole thing so it never goes stale.
For more details, see our Terms of Service.
What Subresource Integrity Actually Does
Subresource Integrity (SRI) tells the browser “only run this script or stylesheet if its bytes match this hash.” If a CDN gets compromised or a version changes unexpectedly, the browser refuses to load it. No match, no execute.
SRI is a tamper-evident seal for <script> and <link rel="stylesheet">.
Generate Correct Hashes (SHA-256/384/512)
Use a stable tool and base64-encode the digest. I default to SHA-384 for a good security/size trade-off.
macOS / Linux (OpenSSL)
# Hash a local file
openssl dgst -sha384 -binary ./assets/js/app.min.js | openssl base64 -A
# Output example:
# 0C9o3qg6m3f4yM...TruncatedForSpace...
Node (cross-platform)
node -e "const fs=require('fs'),c=fs.readFileSync('assets/js/app.min.js');\
const h=require('crypto').createHash('sha384').update(c).digest('base64');\
console.log(h)"
The integrity value becomes sha384-BASE64_HASH
.
Wire SRI Into Your HTML (Correctly)
Add integrity
and crossorigin="anonymous"
to your tags. If bytes change, the resource won’t load.
<link rel="stylesheet"
href="https://cdn.example.com/css/site.min.css"
integrity="sha384-BASE64_HASH"
crossorigin="anonymous">
<script defer
src="https://cdn.example.com/js/app.min.js"
integrity="sha384-BASE64_HASH"
crossorigin="anonymous"></script>
If you self-host, you can still use SRI for defense-in-depth. Use with CSP & modern headers for a layered model.
Pair SRI With a Tight Content-Security-Policy
SRI covers tampering. CSP covers where resources may load from and whether inline code runs. Together, they shut down a lot of supply-chain/XSS shenanigans.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' https://cdn.example.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
Learn more: MDN: CSP, Google CSP Guide.
Automate Hashing in Your Build Pipeline
Humans forget; builds don’t. Hash artifacts during CI and inject the integrity attribute at template render. Here’s a lightweight Node snippet you can adapt for Eleventy/Nunjucks or any static build.
// build/sri.js
const { createHash } = require('crypto');
const { readFileSync, writeFileSync } = require('fs');
const files = [
{ path: 'dist/js/app.min.js', key: 'app_js' },
{ path: 'dist/css/site.min.css', key: 'site_css' },
];
const sri = {};
for (const f of files) {
const buf = readFileSync(f.path);
const b64 = createHash('sha384').update(buf).digest('base64');
sri[f.key] = `sha384-${b64}`;
}
writeFileSync('.cache/sri.json', JSON.stringify(sri, null, 2));
Consume .cache/sri.json
inside your Nunjucks templates to populate integrity
.
Common SRI Gotchas (and Fixes)
- Hash mismatch after deploy: The CDN re-minified your file. Fix by hashing the final bytes you serve.
- Inline scripts + CSP: Prefer external files. If you must inline, use
'nonce-xyz'
and rotate per request. - Cross-origin without
crossorigin
: Addcrossorigin="anonymous"
so the browser can verify. - Version pinning: If the CDN version changes, your SRI breaks (by design). Pin versions or automate updates.
Authoritative References
Where to Go Next
Lock down the browser surface area with HTTP Security Headers, and don’t miss the bigger picture in Nothing Deletes—policies matter as much as code.