# trusted-types-eval, a safer way to allow eval in CSP (/en/blog/trusted-types-eval-csp)



If you need `eval()` or the `Function()` constructor to keep working under a Content Security Policy (CSP), the usual answer has been to add `'unsafe-eval'`. That keyword re-enables string-to-code compilation for any string, which is exactly the behavior most cross-site scripting (XSS) attacks abuse. The `'trusted-types-eval'` keyword is the safer replacement: it permits `eval()` and `Function()` only when Trusted Types are enforced and only when you pass a `TrustedScript` object instead of a raw string.

The short version: if you are deploying Trusted Types and you still have an `eval` sink you cannot remove yet, use `'trusted-types-eval'` in `script-src` instead of `'unsafe-eval'`. On browsers that enforce Trusted Types, code compilation has to go through a policy first. On browsers that do not support Trusted Types, `eval` stays blocked rather than fully open. The rest of this post explains where the keyword lives, how it behaves, and how to wire it up.

New to this part of CSP? [How to build a strong Content Security Policy](/en/blog/how-to-build-a-strong-csp) covers the foundations, and [Why you should never use unsafe-inline in CSP](/en/blog/unsafe-inline-csp) explains the related inline-script problem.

## What CSP blocks by default [#what-csp-blocks-by-default]

A Content Security Policy is an HTTP response header that tells the browser which scripts it is allowed to run. When the policy sets [`script-src`](/en/docs/web-security/policies/content-security-policy/directives/script-src) (or falls back to [`default-src`](/en/docs/web-security/policies/content-security-policy/directives/default-src)), the browser blocks the functions that turn strings into running code:

* `eval()`
* `new Function()` and the `Function` constructor
* the string form of `setTimeout()` and `setInterval()`

That is intentional. These string-compilation sinks are a direct path from injected text to executed JavaScript, so the security header switches them off unless you opt back in.

The two ways to opt back in are very different in risk. One of them, [`'unsafe-eval'`](/en/docs/web-security/policies/content-security-policy/values/csp-keywords), re-enables string compilation for every string with no checks; see [unsafe-eval and how to remove it](/en/blog/unsafe-eval-csp). The other, `'trusted-types-eval'`, is the subject of this post.

## What trusted-types-eval does [#what-trusted-types-eval-does]

`'trusted-types-eval'` is a `script-src` source keyword. It is not a value of `require-trusted-types-for` and it is not a value of `trusted-types`. It belongs in the `script-src` list next to keywords like `'unsafe-eval'` and `'wasm-unsafe-eval'`.

Here is how the two compare:

* `'unsafe-eval'` turns `eval()` and `Function()` back on for any string. High risk, and it works the same whether or not Trusted Types exist.
* `'trusted-types-eval'` turns them back on only when Trusted Types are enforced for scripts, and only when you pass a `TrustedScript` produced by one of your policies, not a plain string.

The practical difference shows up on browsers that do not support Trusted Types. With `'unsafe-eval'`, those browsers run any string you hand to `eval()`, so a missing Trusted Types feature means no protection at all. With `'trusted-types-eval'`, those browsers have nothing that lets eval through, so the sink stays blocked. You get the eval you need where Trusted Types enforce it, and a safe default everywhere else.

One thing to keep clear: `'trusted-types-eval'` on its own does not enable Trusted Types. It only governs the eval and `Function` sinks inside an already-enforced context. Enforcement still comes from [`require-trusted-types-for`](/en/docs/web-security/policies/content-security-policy/directives/require-trusted-types-for).

## How to enforce Trusted Types for eval [#how-to-enforce-trusted-types-for-eval]

Two directives turn Trusted Types on, and the full walkthrough of [how to enable Trusted Types](/en/blog/enable-trusted-types) covers the rollout. The [`require-trusted-types-for`](/en/docs/web-security/policies/content-security-policy/directives/require-trusted-types-for) directive takes the single value `'script'` and makes the browser enforce Trusted Types on DOM injection sinks and on the string-compilation sinks (`eval`, `Function`):

```http
Content-Security-Policy: require-trusted-types-for 'script'
```

The [`trusted-types`](/en/docs/web-security/policies/content-security-policy/directives/trusted-types) directive controls which policy names you are allowed to create. It accepts one or more policy names, plus optional keywords like `'allow-duplicates'` (and `'none'` to forbid all policies, or `*` to allow any unique name):

```http
Content-Security-Policy: trusted-types myPolicy 'allow-duplicates'
```

To allow `eval()` only when Trusted Types are enforced, combine all three in one policy. One directive per line for readability:

```diff
Content-Security-Policy:
-  script-src 'self' 'unsafe-eval';
+  script-src 'self' 'trusted-types-eval';
  require-trusted-types-for 'script';
  trusted-types myPolicy
```

With that policy in place, calling `eval()` with a plain string throws an `EvalError`, even though `'trusted-types-eval'` is present. You have to compile a `TrustedScript` through one of your registered policies first.

## A real example [#a-real-example]

Register a Trusted Types policy that defines `createScript`, then pass its output into `eval()`:

```javascript
// Register a policy that vets the string before it becomes code.
const policy = window.trustedTypes.createPolicy('myPolicy', {
  createScript: (input) => {
    // Validate or transform input here. Returning it as-is is not safe;
    // a policy is only as good as the checks you put in it.
    return input;
  },
});

// A plain string is rejected under enforcement.
eval('a = "hello"'); // throws EvalError

// A TrustedScript from the policy is accepted.
const trusted = policy.createScript('a = "hello"');
eval(trusted); // runs
```

The string still has to pass through `createScript`, which gives you one place to validate or sanitize it. That is the whole value of the approach, and also its limit: the policy can still be written incorrectly, so Trusted Types do not make the code safe on their own. They make sure every compiled string went through a checkpoint you control.

There is one shortcut worth knowing. A policy registered with the reserved name `"default"` runs its `createScript` on any plain string handed to an eval sink, which re-permits plain-string `eval()` across the page. Use it deliberately, because it widens the surface back out toward `'unsafe-eval'` behavior.

## When violations are reported [#when-violations-are-reported]

Trusted Types violations do not introduce a new report type. They arrive as standard CSP violation reports, the same payload you already receive for any blocked script, delivered to whatever reporting endpoint your policy points at. The exact `effectiveDirective` and `sample` values emitted for an eval-specific Trusted Types block are best confirmed with a live capture; the general report shape (`documentURL`, `blockedURL`, `effectiveDirective`, `sample`) is the standard [CSP violation report body](/en/blog/csp-violation-report-fields).

Send those reports somewhere you can read them. Add a reporting endpoint to your policy:

```http
Content-Security-Policy:
  script-src 'self' 'trusted-types-eval';
  require-trusted-types-for 'script';
  trusted-types myPolicy;
  report-to csp-endpoint
```

```http
Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
```

Deploying Trusted Types on a real site usually means turning enforcement on in Report-Only first, watching which sinks fire, and fixing them before you enforce. CentralCSP ingests those reports and groups them so you can see which pages still rely on eval and which policies are being created. You can check a policy for eval keywords and other risks with the free [CSP evaluator](/tools/csp-evaluator) before you ship it.

## Should you use it at all [#should-you-use-it-at-all]

`'trusted-types-eval'` is a safer keyword than `'unsafe-eval'`, but the safest policy has neither. Running code from strings stays risky, and a single mistake in a policy can still let an injection through.

A reasonable order of preference:

1. Remove the eval. Replace dynamic string compilation with `JSON.parse`, a lookup table, or ordinary code wherever you can.
2. Allow specific known scripts. Use a [nonce or a hash](/en/docs/web-security/policies/content-security-policy/values/csp-hashes-nonce) in `script-src` to allow the scripts you trust, rather than opening eval.
3. If you genuinely need eval for now, use `'trusted-types-eval'` with `require-trusted-types-for 'script'` and a strict policy, and treat it as a temporary step while you remove the dependency.

## Browser support today [#browser-support-today]

The `require-trusted-types-for` and `trusted-types` directives recently became available across current Chrome, Firefox, and Safari. [Chromium](https://www.chromium.org/Home/) has shipped Trusted Types for years, and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox) added support more recently.

The `'trusted-types-eval'` keyword is newer, and it recently became available across current Chrome, Firefox, and Safari as a standard-track addition to the CSP Level 3 `script-src` grammar. Because the keyword degrades safely, shipping it on a browser that does not yet recognize it leaves eval blocked, which is the behavior you want.

If you are introducing Trusted Types to an existing app, start in Report-Only, route the reports to a place you can read, and tighten from there. [Get started with CSP reporting](/en/blog/get-started-csp-reporting) walks through the reporting setup, and you can [start free](/register) to see your Trusted Types and eval reports grouped on real traffic.

## Related [#related]

* [Why you should never use unsafe-inline in CSP](/en/blog/unsafe-inline-csp)
* [How to build a strong CSP, step by step](/en/blog/how-to-build-a-strong-csp)
