Configuring SRI for jsDelivr and unpkg
Permalink to "Configuring SRI for jsDelivr and unpkg"Part of CDN Trust Mapping & Routing, this page shows how to pin an exact version on jsDelivr or unpkg and attach a SHA-384 integrity plus crossorigin="anonymous" so a public-registry asset can never execute with unexpected bytes.
Quick Reference
Permalink to "Quick Reference"| Property | jsDelivr | unpkg |
|---|---|---|
| npm URL form | https://cdn.jsdelivr.net/npm/[email protected]/dist/file.js |
https://unpkg.com/[email protected]/dist/file.js |
| GitHub URL form | https://cdn.jsdelivr.net/gh/user/[email protected]/file.js |
not applicable |
| Version pinning | Exact @1.2.3 required for SRI |
Exact @1.2.3 required for SRI |
| Breaks SRI | @latest, @3, no version |
@latest, @3, no version |
| Hash source | “Copy SRI Hash” button on file page | ?meta JSON integrity field |
| CORS header | Access-Control-Allow-Origin: * |
Access-Control-Allow-Origin: * |
| Default algorithm | sha384 |
sha384 |
Pin the exact version, hash the exact URL, and always ship integrity and crossorigin together.
Why Exact Versions Are Non-Negotiable
Permalink to "Why Exact Versions Are Non-Negotiable"Subresource Integrity binds an HTML tag to one specific sequence of bytes. A public registry CDN like jsDelivr or unpkg serves many byte sequences behind a single package name, disambiguated by the version in the URL. When you pin npm/[email protected]/dist/vue.global.prod.js, the CDN treats that URL as immutable and serves identical bytes forever — which is exactly what an integrity token needs. The moment you use a mutable reference such as @latest, @3, or a bare package name, the CDN is free to resolve it to newer bytes on any request. Your frozen sha384-… token then describes an old file the edge no longer serves, and the browser blocks it. This mutability question is the crux of applying SRI to any third-party origin, and it feeds directly into the per-origin policy work in Mapping CDN Origins to SRI Policies.
Both CDNs return permissive CORS headers, so the browser can read the response body to compute the hash — provided the tag opts into a CORS-eligible fetch with crossorigin="anonymous".
The two CDNs resolve URLs differently in ways that matter for integrity. jsDelivr serves from npm (/npm/), GitHub (/gh/), and WordPress (/wp/) namespaces, and every one supports an exact-version pin. unpkg mirrors the npm registry only, resolving [email protected]/dist/file.js to the file inside that published tarball. In both cases the published version is immutable — npm forbids republishing a version’s contents — so a fully pinned URL is a stable hashing target by construction. Where mutability creeps back in is any layer that transforms the tarball’s bytes on the way out: jsDelivr’s on-the-fly minification, jsDelivr’s combine concatenation, or an entrypoint redirect when you omit the file path and let the CDN pick main from package.json. Reference an explicit file path, and the URL you hash is the URL the browser fetches.
Canonical Implementation
Permalink to "Canonical Implementation"A version-pinned jsDelivr script with a SHA-384 integrity token and crossorigin:
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
Get the token straight from the file page’s Copy SRI Hash button on jsdelivr.com, or compute it from the exact URL so you hash precisely what the edge serves:
curl -sL https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js \
| openssl dgst -sha384 -binary | openssl base64 -A
# → prefix the output with sha384- for the integrity attribute
Variant Examples
Permalink to "Variant Examples"unpkg pinned URL with ?meta hash source
Permalink to "unpkg pinned URL with ?meta hash source"unpkg serves the same package tree and exposes per-file digests through its metadata endpoint. Appending ?meta to a pinned URL returns JSON whose file entries include an integrity field:
curl -s 'https://unpkg.com/[email protected]/umd/react.production.min.js?meta'
# → { "path": "/umd/react.production.min.js", "integrity": "sha384-…", ... }
<script
src="https://unpkg.com/[email protected]/umd/react.production.min.js"
integrity="sha384-…"
crossorigin="anonymous"></script>
Stylesheet from jsDelivr
Permalink to "Stylesheet from jsDelivr"The same rules apply to <link> — pin the version, add integrity and crossorigin:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous">
Combined jsDelivr bundle — handle with care
Permalink to "Combined jsDelivr bundle — handle with care"jsDelivr can concatenate several pinned files into one response with the combine path. The digest must be taken over the combined output, and the URL must pin every component version:
<!-- Hash computed over the exact combined response bytes -->
<script
src="https://cdn.jsdelivr.net/combine/npm/[email protected]/dist/vue.global.prod.js,npm/[email protected]/dist/vue-router.global.prod.js"
integrity="sha384-…"
crossorigin="anonymous"></script>
Prefer separate pinned tags where you can: a combined URL couples several packages into one hash, so bumping any one version invalidates the token and forces you to recompute the whole bundle.
Gotchas and Edge Cases
Permalink to "Gotchas and Edge Cases"@latestand range URLs silently break on the next publish.npm/pkg@latest,npm/pkg@3, and barenpm/pkgall resolve to a moving target. The bytes change when the package updates, your frozen token no longer matches, and every user is blocked at once. Only an exact@1.2.3URL is SRI-safe.- Omitting
crossorigin="anonymous"blocks the file even with a correct hash. Without it the browser issues a no-CORS request and receives an opaque response whose body it cannot read. SRI cannot validate opaque responses, so the resource is blocked before the hash is ever compared — a failure that looks nothing like a hash mismatch and is easy to misdiagnose. - jsDelivr auto-minification can change the bytes. When you request a
.min.jspath that the package did not publish, jsDelivr generates a minified version on the fly. If jsDelivr later updates its minifier, those generated bytes can differ, invalidating your token. Pin to a file the package actually publishes, and always hash the exact URL you reference. - Hash the delivered bytes, not a local
node_modulescopy. A local install may differ from the registry tarball the CDN serves (build metadata, line endings). Compute the digest from the live pinned URL, not from disk. - Entrypoint URLs without a file path can redirect.
https://unpkg.com/[email protected](no file) resolves to the package’smainfield via a redirect, and the target can differ between versions. Always reference the explicit file —/umd/react.production.min.js— so the hashed URL is deterministic. - Copy-button hashes still deserve a re-check. The jsDelivr button and unpkg
?metaare conveniences. Re-hashing the live URL in CI guarantees the token matches what the edge serves today.
Verification Steps
Permalink to "Verification Steps"1. Confirm both attributes are present
Permalink to "1. Confirm both attributes are present"grep -E 'cdn\.jsdelivr\.net|unpkg\.com' dist/index.html | grep -E 'integrity=|crossorigin='
Expected — every third-party tag shows a sha384- token adjacent to crossorigin="anonymous".
2. Re-hash the live pinned URL
Permalink to "2. Re-hash the live pinned URL"curl -sL https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js \
| openssl dgst -sha384 -binary | openssl base64 -A
The output must match the token in your HTML exactly. A mismatch means the URL is mutable or the wrong bytes were hashed.
3. Confirm clean browser validation
Permalink to "3. Confirm clean browser validation"Load the page with DevTools open. A stale or wrong token produces:
Failed to find a valid digest in the 'integrity' attribute for resource
'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js' with
computed SHA-384 integrity 'sha384-…'. The resource has been blocked.
No error plus a 200 OK in the Network tab confirms the pin and token are correct.
Related
Permalink to "Related"- CDN Trust Mapping & Routing — parent workflow that classifies public-registry origins into trust tiers and gates deployments
- Mapping CDN Origins to SRI Policies — the per-origin policy matrix that governs which registries may serve which paths
- How to Calculate SHA-256 vs SHA-384 for SRI — computing the
sha384-token from a URL or file