When a CSP includes default-src or script-src, the browser blocks inline scripts, inline event handlers, javascript: URLs and eval() unless you opt back in. Adding unsafe-inline or unsafe-eval works but removes much of the protection. Use a nonce, a hash or refactoring instead.
What is blocked by default
MDN lists the inline cases. A script element with code in the page. An inline event handler attribute such as onerror or onclick. A javascript: URL in a link. The same policy blocks eval(), the Function() constructor and the string form of setTimeout() and setInterval().
These are the usual reasons a page breaks the first time a CSP is enforced, especially one built with older templates or analytics snippets.
The two keywords
| Keyword | What it allows | MDN's guidance |
|---|---|---|
'unsafe-inline' | Inline scripts, handlers and javascript: URLs | Avoid. It defeats much of the purpose of a CSP. |
'unsafe-eval' | eval and similar APIs | Developers should avoid it, for the same reasons. |
MDN notes that inline JavaScript is one of the most common XSS vectors, and that preventing its uncontrolled use is one of the basic goals of a CSP. A policy such as default-src 'self' 'unsafe-inline' therefore restricts hosts but leaves the biggest hole open.
Safer ways to keep inline code
- Nonce. Generate a random value on every response, put it in the policy and on each script tag you author. An injected script lacks the nonce, so it does not run.
- Hash. Compute a SHA-256 hash of an inline script and list it in the policy. Good for static pages, but you must recompute when the script changes.
- Refactor. Move code to external files, and replace inline handlers with
addEventListener()calls.
A useful rule from MDN: if a directive contains nonce or hash expressions, browsers ignore unsafe-inline in it. So you can keep the keyword as a fallback for very old browsers without weakening a modern one. Unlike unsafe-inline, however, unsafe-eval still works in a directive that contains a nonce or hash.
<button id="save">Save</button>
<script nonce="PER-RESPONSE-VALUE">
document.getElementById("save").addEventListener("click", save);
</script>
When eval is hard to remove
Some libraries use eval() or new Function() internally, for example certain template compilers. Options: switch to a build time compiled version, pick a library that does not need eval, or, when no alternative exists, consider the Trusted Types API. MDN describes a trusted-types-eval keyword that only relaxes the block in browsers where Trusted Types is enabled, so the methods stay blocked elsewhere. See Trusted Types explained.
Step by step
- Run the policy in report-only mode and collect violations.
- Sort them into inline handlers, inline scripts and eval users.
- Refactor handlers first, then add nonces or hashes for the inline blocks you keep.
- Replace or isolate eval dependencies.
- Enforce, and remove
unsafe-inlineandunsafe-evalfrom the policy.
For a migration walkthrough, see migrate inline scripts and handlers for a strict CSP.
Try it yourself
Open the SAST code scan demo and look at how a finding for unsafe DOM or eval use is presented with the code line and a suggested fix.
Click through it at your own pace, or open it full screen.
Quick check
Is unsafe-inline ever acceptable?
As a temporary step while migrating, with a plan to remove it. As a permanent setting, MDN says to avoid it.
Does a nonce make unsafe-inline unnecessary?
Yes. Browsers ignore unsafe-inline in a directive that has a nonce or hash, so it only serves as a fallback for browsers that do not support nonces.
Safeguard's code and dependency scanning can surface risky patterns and vulnerable libraries in pull requests. Try the free scan at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.