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:
| Pattern | Example |
|---|---|
| Inline script blocks | A script element with code inside |
| Inline event handlers | onclick or onerror attributes |
| javascript: URLs | A link whose href starts with javascript: |
| eval-like calls | eval, 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
- Turn on report-only mode with a strict policy. See CSP report-only rollout steps.
- Collect violations for a representative period and group them by page and pattern.
- Replace inline handlers. Give the element an id or class and attach the listener from a script file.
- Replace javascript: links with a real URL or a button that has a listener.
- Replace eval, new Function and string timers with functions. Check dependencies too, since some old libraries use them.
- Protect inline blocks you keep, with a nonce or a hash.
- 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
- MDN, Content Security Policy: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
Last reviewed: 19 October 2026.