Web Security

Can I Use SRI With Dynamic Content or Inline Scripts?

Learn about Subresource Integrity limitations with dynamic content, inline scripts, and practical approaches to securing dynamic resources.

By Inventive HQ Team

Can You Use SRI With Dynamic Content or Inline Scripts?

No—Subresource Integrity (SRI) cannot protect dynamic content or inline scripts, because it verifies a resource against a cryptographic hash of its exact bytes that you must know in advance. Dynamic scripts produce different bytes on each response (so the hash never matches), and inline <script> blocks have no src for an integrity attribute to attach to. SRI is built for static external files—CDN-hosted libraries, CSS frameworks, and fonts that don't change between requests. For anything inline or dynamic, use Content-Security-Policy (CSP) hashes or nonces instead, and pair the two: SRI for static assets, CSP for everything else.

That's the summary an AI Overview would give you. Here's what it can't: the exact decision tree for your resource, a scenario-by-scenario table of what works and what to use instead, and a live tool to generate the hashes—so you can act on the answer instead of just reading it.

Understanding SRI Limitations With Dynamic Content

Subresource Integrity (SRI) provides a powerful security mechanism by allowing you to specify cryptographic hashes for external resources. However, SRI has fundamental limitations when applied to dynamic content—content that changes between requests. Understanding these limitations and working within them is crucial for using SRI effectively in modern applications.

SRI decision flow: is your resource static or dynamic? A decision diagram showing that static external files can use SRI, while inline and dynamic scripts must use CSP hashes or nonces instead. Can this resource use SRI? Do you know its exact bytes ahead? YES · static CDN lib, CSS, font Use SRI: integrity="sha384-…" NO · dynamic/inline Per-user JS, inline block Use CSP hash or nonce Best practice: combine both SRI locks static CDN files · CSP nonces/hashes cover inline & dynamic scripts

Which Scenario Are You In? SRI vs. the Alternative

The whole question comes down to one property: can you know the resource's exact bytes before the HTML is written? If yes, SRI works. If the bytes vary per request—or there's no src to attach to—SRI can't help and you reach for CSP instead.

ScenarioDoes SRI work?WhyUse instead
Static CDN library (jQuery, Bootstrap)✅ YesBytes are fixed and knowable in advanceSRI integrity hash
CSS framework / web font from CDN✅ YesStatic file, stable hashSRI on <link>
Build-tool output (Webpack, Next.js)✅ YesHash computed at build time, injected into tagsAutomated SRI
Inline <script> block❌ NoNo src attribute to carry integrityCSP hash (static) or nonce (dynamic)
Per-user / server-generated JS❌ NoBytes differ per response, hash never matchesCSP nonce + strict-dynamic
Template-rendered script ({{ user.id }})❌ NoContent varies per requestCSP nonce; move static part to a file
API endpoint returning JS (/config.js?user=123)❌ NoResponse changes per user/requestCSP script-src allowlist; fetch config as data
Content negotiated by Accept header⚠️ FragileBytes may differ by negotiationPin one variant or use CSP

How SRI Works: The Hash-Based Verification

SRI works by computing a hash of a resource's content when it's served from a CDN or external source. You embed this hash in your HTML as an integrity attribute:

<script src="https://cdn.example.com/script.js" integrity="sha256-abc123..."></script>

When the browser downloads this script, it computes the hash and verifies it matches the integrity attribute. If they don't match (because someone modified the script), the browser rejects it.

This hash-based approach is secure and reliable—but only if the content is static. If the content changes, the hash changes, and the integrity verification fails.

Why Dynamic Content Breaks SRI

Dynamic content is any resource that changes between requests. Examples include:

Server-generated JavaScript - A script that changes based on server-side state:

window.API_KEY = "dynamically-generated-key";
window.USER_ID = {{ current_user.id }};

Each user might get different content, or the same user might get different content on different visits. The hash changes with each response, making SRI impossible.

Template-rendered Content - Resources that include dynamic template variables:

<script src="/api/config.js?version={{ app_version }}"></script>

The query parameter might change, making the content different each time.

Negotiated Content - Resources served in different formats based on Accept headers or other request characteristics. The content might differ between requests in ways that change the hash.

The Core Problem: You Can't Hash Dynamic Content

The fundamental issue is that SRI requires you to know the hash of the content before specifying it in your HTML. With dynamic content, you can't know what the hash will be until the content is generated, and by then it's too late to embed it in the HTML's integrity attribute.

This is why SRI is specifically designed for static external resources: JavaScript libraries from CDNs, CSS frameworks, fonts, and similar content that doesn't change between requests.

Advertisement

Alternative Approaches for Dynamic Scripts

If your application requires dynamic scripts, consider these alternatives:

Use Content-Security-Policy (CSP) instead of SRI - CSP can restrict which external resources can be loaded and what inline scripts can execute. While not hash-based like SRI, CSP provides security against malicious script injection:

<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://trusted-cdn.example.com">

This allows scripts only from your own origin and specific trusted CDNs, preventing injection of malicious scripts from unexpected sources.

Generate SRI Hashes at Build Time - If your dynamic content is actually static-generated-with-variables, you can compute hashes during your build process:

// build-script.js
const fs = require('fs');
const crypto = require('crypto');

function computeSRI(content) {
  const hash = crypto.createHash('sha256');
  hash.update(content);
  return `sha256-${hash.digest('base64')}`;
}

const configScript = `window.API_KEY = "${process.env.API_KEY}";`;
const integrity = computeSRI(configScript);
console.log(`<script integrity="${integrity}">${configScript}</script>`);

This approach works if your dynamic content is generated during your build process rather than at request time.

Once you have the final, static bytes of a resource, you can generate its SRI hash directly below—paste the file contents or URL and copy the ready-to-use integrity attribute:

Loading interactive tool...

Use Subresources with Query Strings Carefully - If you must use query strings in resource URLs:

<script src="https://cdn.example.com/config.js?version=abc123" integrity="sha256-..."></script>

You can compute the integrity hash based on the full URL with query string, but you must recompute and update your HTML whenever the query parameter changes.

Inline Scripts and SRI

SRI doesn't directly protect inline scripts (scripts embedded directly in HTML):

<!-- This inline script cannot use integrity attribute -->
<script>
  console.log('inline script');
</script>

Inline scripts don't have a src attribute, so you can't add an integrity attribute to them. However, you can:

Use CSP nonces for inline scripts - Include a nonce (a random value) in your CSP and embed it in your inline script:

<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc123'">
<script nonce="abc123">
  console.log('This inline script is allowed');
</script>

<script>
  console.log('This inline script is blocked - no nonce');
</script>

The nonce is generated per request and embedded in both the CSP header and the script tag. Since attackers can't predict the nonce, they can't inject malicious inline scripts.

Use CSP hashes for static inline scripts - If your inline script content never changes, you can use a hash in CSP:

<meta http-equiv="Content-Security-Policy" content="script-src 'sha256-abc123'">
<script>
  const fixed = 'content that never changes';
</script>

This is secure for truly static inline code, but not for content that changes.

Move inline scripts to external files - The best approach is avoiding inline scripts altogether by externalizing them and using SRI:

<!-- Move the inline script to an external file -->
<script src="https://example.com/scripts/my-script.js" integrity="sha256-..."></script>

This allows using SRI properly and is generally considered a security best practice.

Practical Patterns for Hybrid Static-Dynamic Content

Some applications have resources that are mostly static but include small dynamic sections. Consider these patterns:

Pattern 1: Separate Static and Dynamic

<!-- Static external script with SRI -->
<script src="https://example.com/lib/app.js" integrity="sha256-..."></script>

<!-- Dynamic configuration inline (without SRI, secured by CSP nonce) -->
<script nonce="abc123">
  window.CONFIG = {{ dynamic_config | safe }};
</script>

Pattern 2: Load Dynamic Config Separately

<!-- Static app script with SRI -->
<script src="https://example.com/app.js" integrity="sha256-..."></script>

<!-- Dynamic config fetched separately -->
<script>
  fetch('/api/config')
    .then(r => r.json())
    .then(config => window.CONFIG = config);
</script>

The static app code is protected by SRI, and dynamic config is loaded separately without SRI.

Pattern 3: Server-Side Computed Hashes

<!-- Template computes hash of dynamic content -->
<script src="https://cdn.example.com/config.js" integrity="sha256-{{ config_hash }}"></script>

Your server computes the hash of the generated config.js and embeds it. This works if you regenerate the hash whenever content changes.

SRI With Service Workers and Caching

If your application uses service workers, they can affect SRI verification:

Service workers can intercept requests and return cached content. SRI verification still works—the browser verifies the content the service worker returns, not the original resource.

Ensure cached content is fresh - If a service worker caches content that later changes, the integrity hash becomes invalid. Your caching strategy must account for this.

Use versioning with SRI - Include version identifiers in your URLs to bust caches when content changes:

<script src="https://example.com/script-v2.js" integrity="sha256-..."></script>

When you update the script, increment the version number, change the URL, and update the integrity hash.

Real-World Considerations

APIs that return JavaScript - If your API returns dynamically-generated JavaScript:

GET /api/user-config.js?user=123
Content: window.USER_ID = 123;

You cannot use SRI on this endpoint because the content changes per user/request.

Server-Side Template Rendering - If your templates render JavaScript:

<script>
  var userId = <%= current_user.id %>;
</script>

This cannot use SRI because content varies per request.

Frameworks That Auto-Generate Hashes - Some frameworks can compute and embed SRI hashes automatically:

<!-- Webpack/build tool output -->
<script src="https://example.com/app.hash123.js" integrity="sha256-abc..."></script>

Frameworks like Next.js can compute hashes during build and embed them automatically.

Best Practices for SRI With Dynamic Applications

  1. Use SRI for truly static external resources - CDN-hosted libraries, frameworks, fonts
  2. Use CSP for dynamic or inline scripts - Nonces or hashes depending on your needs
  3. Separate static from dynamic - Keep statically served content in files you can hash
  4. Automate hash computation - Don't manually manage SRI hashes; use build tools
  5. Combine SRI and CSP - Use both for comprehensive protection
  6. Document your approach - Note which resources use SRI and why dynamic ones don't
  7. Test thoroughly - Verify that your security controls actually work in your application

Conclusion: SRI Works Best With Static Content

SRI is a powerful security tool, but its hash-based approach fundamentally requires static content. Dynamic content is incompatible with SRI because you can't know the hash in advance. For dynamic resources, use CSP with nonces or other security strategies. In modern applications, the ideal approach combines SRI for static external resources and CSP for anything dynamic or inline, creating comprehensive protection against script injection attacks.

Frequently Asked Questions

Can I use SRI with inline scripts?

No. Subresource Integrity only applies to resources loaded via a src or href attribute (<script>, <link>), so an inline <script> block has nothing for the integrity attribute to check. To pin an inline script, use a Content-Security-Policy hash ('sha256-…') for code that never changes, or a per-request nonce ('nonce-…') for code that does. The cleanest fix is to move the inline code into an external file and apply SRI to that.

Does SRI work with dynamically generated JavaScript?

No. SRI requires you to know the exact byte-for-byte hash of the resource before you write the HTML. Dynamically generated scripts—per-user config, template-rendered values, content negotiated by request—produce a different hash on each response, so the integrity check would fail. Secure dynamic scripts with CSP nonces instead, and split the static, hashable part into a separate file.

What is the difference between SRI and CSP for script security?

SRI verifies that a specific external file matches a known hash—it answers "is this exact file untampered?" CSP controls which sources and inline scripts are allowed to run at all—it answers "is this script permitted to execute?" SRI cannot cover inline or dynamic scripts; CSP hashes and nonces can. They are complementary: use SRI for static CDN assets and CSP for everything inline or dynamic.

Why does my SRI hash keep failing after the file changes?

SRI hashes the exact bytes of the delivered resource. If the file content changes—even whitespace, a version query string that alters output, or a CDN re-minifying it—the computed hash no longer matches the integrity value and the browser blocks the resource. Recompute the hash whenever the file changes and version the URL (e.g. app-v2.js) so caches update together.

Can a build tool generate SRI hashes automatically?

Yes. Bundlers and frameworks such as Webpack (via plugins) and Next.js can compute SRI hashes at build time and inject them into the emitted <script> and <link> tags. This works because the output is finalized during the build, so its bytes—and therefore its hash—are known before the HTML ships. Build-time generation is the recommended way to manage SRI at scale.

How do CSP nonces secure inline scripts if SRI can't?

A nonce is a random, unguessable token generated fresh for every HTTP response. The server places it in both the CSP header (script-src 'nonce-abc123') and the nonce attribute of each trusted inline script. The browser runs only inline scripts whose nonce matches, and because an attacker injecting markup cannot predict the value, injected scripts are blocked—without needing a content hash.

Should I use a CSP hash or a nonce for inline scripts?

Use a CSP hash ('sha256-…') when the inline script content is fixed and never changes—the hash is static and needs no server logic. Use a nonce when the inline script contains dynamic values or you cannot precompute a hash; the nonce must be regenerated per request and requires server-side support. Many strict CSPs combine nonces with strict-dynamic for script-heavy apps.

Does SRI work with service workers and caching?

Yes—SRI verifies whatever bytes the browser ultimately receives, including content returned by a service worker from cache. The catch is staleness: if a service worker serves an old cached file whose bytes no longer match the integrity value in updated HTML, the browser rejects it. Version your URLs so cache entries and integrity hashes rotate together.

srisubresource-integritydynamic-contentweb-security