# What unsafe-eval does in CSP and how to remove it (/en/blog/unsafe-eval-csp)





If your Content Security Policy (CSP) contains `'unsafe-eval'`, you are letting the page turn arbitrary strings into running code. CSP is an HTTP response header that tells the browser which scripts it is allowed to run, and by default it blocks the functions that compile a string into executable JavaScript. The `'unsafe-eval'` keyword switches that block back off.

The short version: do not ship `'unsafe-eval'` in a script directive. Find what depends on it (usually a framework feature or an injected snippet), move to a build that does not need runtime code compilation, and if you only need WebAssembly, use the narrower `'wasm-unsafe-eval'` instead. The rest of this post explains what the keyword allows, why it weakens the policy, where it tends to sneak in, and how to migrate off it with Report-Only first.

New to CSP? [Get started with Content Security Policy](/en/blog/get-started-with-csp) covers the basics first. For the related inline-script problem, see [why you should never use unsafe-inline in CSP](/en/blog/unsafe-inline-csp).

## What does 'unsafe-eval' do? [#what-does-unsafe-eval-do]

By default, a CSP that sets `script-src` blocks every API that compiles a string into code. Adding `'unsafe-eval'` to that directive re-enables them. The functions it unblocks are:

* `eval("...")`, which runs a string as JavaScript.
* The `Function` constructor, `new Function("a", "return a + 1")`, which builds a function from string source.
* `setTimeout("code", 0)` and `setInterval("code", 0)` when the first argument is a string rather than a function.
* Compiling and instantiating WebAssembly, which historically also required `'unsafe-eval'`.

When `'unsafe-eval'` is absent, the browser throws on each of these and reports a violation. Passing a function to `setTimeout` keeps working; only the string form is affected.

## Why it weakens the policy [#why-it-weakens-the-policy]

The whole point of a strict CSP is that the browser runs only code you marked as trusted, through a nonce or a hash. String-to-code APIs route around that. Once `eval` is allowed, any attacker-controlled string that reaches an `eval` sink becomes running code, which is exactly the foothold most cross-site scripting (XSS) attacks want.

A real-world chain looks like this: user input lands in a value that some library later passes to `eval` or `new Function` (a template compiler, an expression evaluator, a config parser). Without `'unsafe-eval'` the browser refuses to compile it. With `'unsafe-eval'` present, the injected string runs with full page privileges, and the nonce or hash protection you set up does nothing to stop it.

CSP is defense in depth, not a replacement for input handling. You still validate and escape. `'unsafe-eval'` removes the layer that contains the damage when something slips through.

## Where 'unsafe-eval' sneaks in [#where-unsafe-eval-sneaks-in]

Most teams do not add `'unsafe-eval'` on purpose. It creeps in because a tool needs it:

* **Front-end frameworks in JIT mode.** [Angular](https://angular.dev/) with the just-in-time compiler compiles templates at runtime through code generation. [Vue](https://vuejs.org/) with the runtime compiler build (templates compiled in the browser) does the same. Both need `'unsafe-eval'` only in those modes.
* **Tag managers with custom JavaScript.** [Google Tag Manager](https://developers.google.com/tag-platform/tag-manager) custom HTML and custom JavaScript variables evaluate strings, which pulls in `'unsafe-eval'`. See [running Google Analytics and Tag Manager under a strict CSP](/en/blog/csp-google-analytics-tag-manager) for the workarounds.
* **Older bundlers and dev builds.** Some development builds and source-map setups use `eval` to wrap modules. Production builds usually do not, so this is often a dev-only leak that should never reach production headers.
* **Charting, expression, and templating libraries.** A library that lets users write formulas or templates may compile them with `new Function`. Check the library before allowing `eval` for it.
* **WebAssembly.** Compiling a `.wasm` module used to count as an `eval` for CSP purposes, so any WASM-using code dragged in `'unsafe-eval'`.

## How to remove it [#how-to-remove-it]

The fix is almost always to stop compiling code at runtime, not to keep the keyword. If you cannot remove an eval sink yet, [enable Trusted Types](/en/blog/enable-trusted-types) so the remaining string compilation has to pass through a policy first.

### Switch frameworks to ahead-of-time (AOT) builds [#switch-frameworks-to-ahead-of-time-aot-builds]

Angular's default production build is AOT: templates are compiled during the build, so the browser never compiles anything and `'unsafe-eval'` is not needed. Make sure you are not shipping a JIT build to production. For Vue, use the runtime-only build and precompile templates with the build tooling rather than the runtime compiler.

```http
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m' 'strict-dynamic'
```

That is the target: a script directive with no `'unsafe-eval'` at all, trusting scripts through a nonce and `'strict-dynamic'`. For how that keyword propagates trust, see [the strict-dynamic guide](/en/blog/strict-dynamic-csp).

### Replace eval-based code [#replace-eval-based-code]

For your own code, the string forms have direct replacements:

```javascript
// Before: needs 'unsafe-eval'
setTimeout("doWork()", 1000); // [!code --]
const add = new Function("a", "b", "return a + b"); // [!code --]

// After: works under a strict policy, no 'unsafe-eval'
setTimeout(() => doWork(), 1000); // [!code ++]
const add = (a, b) => a + b; // [!code ++]
```

Pass a function to `setTimeout` and `setInterval`. Replace `new Function` with a real function or a small lookup table of known operations. Parse JSON config with `JSON.parse`, never `eval`.

### Move template or expression evaluation to build time [#move-template-or-expression-evaluation-to-build-time]

If a library compiles templates or expressions in the browser, switch to its precompiled mode or precompute the values during your build, so nothing reaches a runtime `eval` sink.

## The narrower option, 'wasm-unsafe-eval' [#the-narrower-option-wasm-unsafe-eval]

If the only reason you needed `'unsafe-eval'` was WebAssembly, there is a much smaller keyword. `'wasm-unsafe-eval'` allows compiling and instantiating WASM modules but does **not** re-enable `eval`, `new Function`, or string timers. It is the right choice when you ship a WASM module and want everything else to stay blocked.

```http
Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'
```

Use `'wasm-unsafe-eval'` instead of `'unsafe-eval'` whenever WASM is your only need. It is widely supported in current browsers. `'unsafe-eval'` does permit WebAssembly compilation, but it also re-enables `eval`, `new Function`, and string timers, so using it just for WASM is wider than necessary. `'wasm-unsafe-eval'` is the narrower keyword that allows only WASM compilation and leaves everything else blocked.

<img alt="Violation rows whose blocked origin reads eval rather than a host" src="__img0" width="1359" height="173" />

## A safe migration path off 'unsafe-eval' [#a-safe-migration-path-off-unsafe-eval]

Removing `'unsafe-eval'` can break anything that quietly relied on it, so move in stages and watch reports before you enforce. This is the same Report-Only-first approach as [enforce vs Report-Only mode](/en/blog/csp-enforce-vs-report-only).

1. **Find the eval usage.** Search your bundle and dependencies for `eval(`, `new Function(`, and string `setTimeout`/`setInterval`. The [CSP evaluator](/tools/csp-evaluator) flags when a live policy still carries `'unsafe-eval'`.
2. **Fix each case.** Move frameworks to AOT, replace string timers and `new Function`, and switch WASM to `'wasm-unsafe-eval'`.
3. **Test in Report-Only.** Ship the policy without `'unsafe-eval'` on the `Content-Security-Policy-Report-Only` header. The browser blocks nothing, it only reports what it would block, so you see every remaining eval sink on real traffic.
4. **Enforce.** When Report-Only is quiet, move the same policy to the enforcing `Content-Security-Policy` header.

CentralCSP collects those Report-Only reports for you, groups them, and shows the scripts running on each page, so you can pinpoint which dependency is still calling `eval` before you flip to enforce. That is the core of the [CSP suite](/platform/csp-builder). You can [start a free trial](/register) and point a Report-Only header at it to watch the migration.

For the full directive and keyword reference, see the [CSP policy reference](/en/docs/web-security/policies/content-security-policy).

## Frequently asked questions [#frequently-asked-questions]

### Is 'unsafe-eval' safe to keep? [#is-unsafe-eval-safe-to-keep]

No. It re-enables string-to-code compilation for any string, which is the behavior most XSS attacks abuse. Remove it and use AOT builds, plain functions, and `'wasm-unsafe-eval'` for WebAssembly.

### What is the difference between 'unsafe-eval' and 'wasm-unsafe-eval'? [#what-is-the-difference-between-unsafe-eval-and-wasm-unsafe-eval]

`'unsafe-eval'` allows `eval`, `new Function`, and string timers as well as WebAssembly. `'wasm-unsafe-eval'` allows only WebAssembly compilation and leaves the rest blocked, so it is the narrower, safer choice when WASM is all you need.

### Does Angular need 'unsafe-eval'? [#does-angular-need-unsafe-eval]

Only in just-in-time mode. The default production AOT build compiles templates at build time, so it does not need `'unsafe-eval'`. Confirm you are not shipping a JIT build to production.

## Sources [#sources]

* [MDN, script-src directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src)
* [MDN, CSP guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)
* [W3C CSP Level 3 specification](https://www.w3.org/TR/CSP3/)
* [Angular security guide](https://angular.dev/best-practices/security)

## Related [#related]

* [Why you should never use unsafe-inline in CSP](/en/blog/unsafe-inline-csp)
* [strict-dynamic explained](/en/blog/strict-dynamic-csp)
* [trusted-types-eval, a safer way to allow eval in CSP](/en/blog/trusted-types-eval-csp)
