How this check works
Scanaris walks through the HTML of your main page and looks at two kinds of tag: <script src="..."> and <link rel="stylesheet" href="...">. It resolves each URL against the page's and treats it as external if its origin (scheme, domain and port) differs from yours; that includes URLs starting with //, which load from the CDN with the page's protocol. It ignores data: and blob:. For each external resource it checks for the integrity attribute; if missing, it notes it. If there is at least one, it opens a low-severity finding with the number of resources and the first ten URLs. Scripts and styles from your own domain are not flagged: SRI adds nothing there.
Why it matters
When you load a library from a CDN, you are running in your users' browsers whatever that CDN decides to serve. If the CDN is compromised, or if its domain changes hands, the file can be replaced by one that does the same thing and also steals form data or redirects to scams. It happened in June 2024 with polyfill.io: the domain was sold, the script started injecting malicious code and more than a hundred thousand sites that included it served it without noticing. The integrity attribute holds the hash of the file you reviewed; the browser hashes what it downloads and, if it does not match, refuses to run it. It is low severity because it requires the CDN to be attacked first, but when that happens the damage is total and silent.
How to fix it
Option 1: add integrity to the tags
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"
integrity="sha384-..." crossorigin="anonymous"></script>
You get the hash by downloading the exact file and running:
curl -s https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js | openssl dgst -sha384 -binary | openssl base64 -A
cdnjs and jsDelivr show a ready-to-copy hash next to each file. The crossorigin="anonymous" attribute is required so the browser can verify a resource from another origin. And the URL must point at a pinned version (@4.4.0), never at latest: if the CDN updates the file, the hash stops matching and your site stops loading that library, which is exactly what you want to happen on an unreviewed change.
Option 2: serve the libraries from your own domain
Install them with npm and let your bundler (Vite, webpack, Nuxt, Next.js) include them in your build. The file then comes from your server, with a hash in its name, and SRI is no longer needed. It is the usual choice in modern projects and the one that resolves the finding at the root.
What cannot use SRI
Google Fonts stylesheets change depending on the browser requesting them, so their hash is not stable: either accept that finding or self-host the fonts (which also improves privacy and speed). Analytics and ad scripts that update themselves (gtag.js, the Meta pixel) cannot take a fixed hash either; there the defence is CSP, which limits what each origin can do and is measured by weak CSP.
Check it is fixed
curl -s https://yourdomain.com | grep -oE '<(script|link)[^>]*(src|href)="(https?:)?//[^"]+"[^>]*>' | grep -v integrity
Every line that comes out is an external resource without integrity. If only the ones that cannot take it remain (fonts, analytics), you are done.
Frequently asked questions
Does SRI slow down loading?
Not noticeably: computing a SHA-384 hash of a file a few hundred kilobytes in size takes milliseconds. What it does do is force you to pin versions, which is good practice anyway.
If I update the library, do I have to change the hash?
Yes, and that is the point: the update goes through your hands. If you use npm and your bundler, the process is the same as always and the build handles the hash.
What about old versions of libraries on the CDN?
SRI guarantees the file is the one you chose, not that it is safe. A jQuery 1.12 with the correct hash still has its flaws; that is detected by outdated libraries.