Safeguard
Developer Tools•

How do you remove inline scripts and handlers to adopt a strict CSP?

Move inline event handlers into addEventListener, drop javascript: URLs and eval, then protect remaining inline blocks with a nonce or hash.

Safeguard Security Team
3 min read

Find every inline event handler, javascript: URL and eval-style call, and replace them with code in script files using addEventListener. Keep any inline script blocks you need, but protect them with a per-response nonce or a hash. Then enforce the policy after a report-only trial.

What a policy blocks by default

MDN says that when a policy includes default-src or script-src, these are blocked unless you opt out:

PatternExample
Inline script blocksA script element with code inside
Inline event handlersonclick or onerror attributes
javascript: URLsA link whose href starts with javascript:
eval-like callseval, new Function, string argument to setTimeout

You can re-enable inline code with unsafe-inline, but MDN says developers should avoid it. Browsers ignore unsafe-inline when a nonce or hash is present in the same directive. unsafe-eval is similar in spirit, and unlike unsafe-inline it still works alongside nonces.

Step by step

  1. Turn on report-only mode with a strict policy. See CSP report-only rollout steps.
  2. Collect violations for a representative period and group them by page and pattern.
  3. Replace inline handlers. Give the element an id or class and attach the listener from a script file.
  4. Replace javascript: links with a real URL or a button that has a listener.
  5. Replace eval, new Function and string timers with functions. Check dependencies too, since some old libraries use them.
  6. Protect inline blocks you keep, with a nonce or a hash.
  7. Enforce, and keep reporting on.

The pattern in code

Before:

<button onclick="save()">Save</button>

After:

<button id="save-button">Save</button>
<script nonce="RANDOM_PER_RESPONSE">
  document.getElementById("save-button").addEventListener("click", save);
</script>

MDN's own example does the same thing, swapping an onclick for addEventListener inside an inline block that is allowed by hash.

Nonce or hash for what remains

A nonce must be new and unpredictable on every response, so your server has to template it into the policy and the tag. That rules out fully static HTML. A hash covers a fixed script, and any edit means a new hash. Comparison in CSP nonce vs hash vs host allowlist.

Things that surprise teams

  • Third party widgets that inject inline styles or handlers will show up in reports. Decide to replace, isolate or allow each one.
  • Templating frameworks sometimes emit inline handlers from components. Fix the component once, not each page.
  • Old dependencies may need eval. If one does, upgrade or replace it rather than allow unsafe-eval globally.
  • Report noise from browser extensions is normal. Filter by source file.

Safeguard's SAST code scanning can help locate risky patterns in your source before you start, and dependency scanning flags aging packages. Try a free scan at gold.safeguard.sh/scan.

Try it yourself

Click through it at your own pace, or open it full screen. Look at how a code finding points to a file and line you could fix.

Quick check

Can I keep inline scripts and still use a strict CSP?

Yes, if each inline block carries a valid nonce or matches a listed hash. Inline event handlers cannot be saved this way and must be rewritten.

Is unsafe-inline ever fine?

MDN advises avoiding it. It removes most of the protection CSP gives against injected script.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.