Trusted Types / require-trusted-types-for: stopping DOM XSS sinks

Chrome reports “This document requires ‘TrustedHTML’ assignment” when your code sets innerHTML with a plain string. Trusted Types force every risky DOM sink to go through a named policy you control.

What Trusted Types are for

Classic CSP stops the browser loading or running unexpected scripts. DOM XSS often bypasses that: the page already ran your code, then that code did el.innerHTML = userInput. Trusted Types make those sinks type-safe. With the policy on, a bare string is rejected; only a TrustedHTML, TrustedScript or TrustedScriptURL created by a policy you registered may be assigned.

The CSP directives

Content-Security-Policy:
  require-trusted-types-for 'script';
  trusted-types dompurify default;
DirectiveMeaning
require-trusted-types-for 'script'Enforce Trusted Types on script-ish sinks: innerHTML, outerHTML, insertAdjacentHTML, document.write, eval/Function (where applicable), script.src, script.text, and similar
trusted-types name1 name2Allow-list of policy names trustedTypes.createPolicy may register
trusted-types defaultAllows a policy literally named default, used automatically by some sinks / libraries
trusted-types *Any policy name (weak; prefer an explicit list)
trusted-types 'allow-duplicates'Lets the same name be created twice (useful with duplicate bundles); combine with names

Support is strongest in Chromium. Firefox and Safari are catching up; unsupported browsers ignore the directives, so keep your sanitiser logic anyway.

Minimal policy example

// Only after the CSP allows this policy name:
const policy = trustedTypes.createPolicy('dompurify', {
  createHTML: (dirty) => DOMPurify.sanitize(dirty, {RETURN_TRUSTED_TYPE: false}),
  createScriptURL: (url) => {
    const u = new URL(url, location.href);
    if (u.origin !== location.origin) throw new TypeError('blocked script URL');
    return u.href;
  },
});

el.innerHTML = policy.createHTML(userHtml);   // TrustedHTML
s.src = policy.createScriptURL('/js/widget.js');

Never write a policy that returns its input unchanged (createHTML: s => s) unless the value is already known-safe constants — that reopens the sink.

Console errors you will see

Rollout tips

  1. Start with Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; trusted-types default dompurify (see Report-Only vs Enforce).
  2. Fix first-party code and known libraries (DOMPurify, lit, Angular have Trusted Types modes).
  3. Third-party widgets that assign innerHTML freely may need a narrow policy or to be sandboxed in an iframe with a looser policy.
  4. Enforce once reports are quiet. Keep a normal script-src policy too — Trusted Types complement CSP, they do not replace it. Check the whole header in the CSP explainer.

Related

CSP header explainer · CSP Report-Only vs Enforce · Refused to execute inline script · Refused to connect (connect-src) · Anvil CSP checker API

Questions

What is require-trusted-types-for 'script'?

A CSP directive that makes the browser reject raw strings in dangerous DOM sinks like innerHTML and script.src unless they are Trusted Types created by an allowed policy.

Do I still need script-src if I use Trusted Types?

Yes. Trusted Types stop DOM XSS sinks; script-src still controls which scripts can load and run.

Why does createPolicy throw 'disallowed'?

Your CSP trusted-types list does not include that policy name. Add the name, or use trusted-types * only while debugging.

Does Trusted Types work in Firefox and Safari?

Chromium has had support longest. Other engines are adding it; feature-detect trustedTypes and keep sanitising either way.

Is a passthrough createHTML: s => s safe?

No. That disables the protection. The policy must sanitise, allow-list constants, or otherwise prove the value is safe.

Published .

Try it

Related guides

Newest guides