How to Configure Strict Content-Security-Policy (CSP) with require-trusted-types-for in Google Chrome

Cross-Site Scripting (XSS) originating in the DOM remains one of the most pervasive vulnerabilities in modern client-side single-page applications. While classic Content Security Policy (CSP) mitigates reflected and stored XSS by restricting script origins and blocking inline scripts, it historically struggled to prevent vulnerable DOM sinks (such as Element.innerHTML, document.write, or location.href) from executing unsanitised strings passed via client-side routing. The W3C Trusted Types specification resolves this systemic flaw. By configuring require-trusted-types-for 'script' in your CSP header, Google Chrome disables raw string injection into all dangerous DOM execution sinks.

Understanding Trusted Types and DOM Execution Sinks

Trusted Types transforms browser DOM APIs from accepting arbitrary string primitives into accepting only cryptographically typed policy objects:

  • require-trusted-types-for 'script': Instructs Google Chrome to throw a runtime TypeError whenever an application passes a raw string to an injection sink (such as innerHTML, outerHTML, script.src, or Worker()).
  • Trusted Type Objects: The browser recognizes three distinct wrapper types: TrustedHTML, TrustedScript, and TrustedScriptURL.
  • trustedTypes.createPolicy(): The only sanctioned mechanism to produce trusted objects. Enforces centralized sanitisation logic (using DOMPurify or custom token sanitisation) before any HTML enters the DOM tree.
  • trusted-types [policyNames]: Restricts which policy names are authorized to create trusted objects, preventing rogue third-party dependencies from creating bypass policies.

Configuring Server Headers in Nginx and Express

Deploy the strict Trusted Types directive as part of your application’s Content-Security-Policy HTTP response header:

# Nginx Production Configuration
server {
    listen 443 ssl http2;
    server_name portal.example.com;

    # Enforce Trusted Types for all script injection sinks
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-rAnd0m123'; require-trusted-types-for 'script'; trusted-types dompurify-policy default;" always;

    # Defense-in-depth headers
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;

    location / {
        proxy_pass http://frontend_upstream;
        proxy_set_header Host $host;
    }
}

Authoring the Trusted Types Sanitisation Policy in JavaScript

Implement a centralized DOMPurify sanitisation policy in your application bootstrap routine:

// 1. Verify browser support for Trusted Types
if (window.trustedTypes && trustedTypes.createPolicy) {
    // 2. Create authorized sanitisation policy
    const sanitizerPolicy = trustedTypes.createPolicy('dompurify-policy', {
        createHTML: (dirtyInput) => {
            // Strip dangerous tags, event handlers, and script payloads
            return DOMPurify.sanitize(dirtyInput, { RETURN_TRUSTED_TYPE: false });
        },
        createScriptURL: (url) => {
            // Validate allowed CDN hosts
            const parsed = new URL(url, window.location.origin);
            if (parsed.origin === window.location.origin || parsed.origin === 'https://cdn.example.com') {
                return url;
            }
            throw new TypeError("Blocked untrusted script URL: " + url);
        }
    });

    // 3. Inject safe HTML into the DOM sink using the policy
    function renderUserComment(element, rawUserText) {
        // Correct: Passes a TrustedHTML object produced by the policy
        element.innerHTML = sanitizerPolicy.createHTML(rawUserText);
    }
} else {
    console.warn("Trusted Types not natively supported. Relying on fallback sanitisation.");
}

Debugging Trusted Types Violations in Chrome DevTools

Google Chrome provides specialized developer tools to trace and pinpoint Trusted Types failures:

  1. Open Google Chrome, navigate to your target web application, and open DevTools via F12.
  2. Select the Console tab. If any library attempts to assign a raw string to innerHTML, Chrome displays an explicit security error:
    Uncaught TypeError: Failed to set the 'innerHTML' property on 'Element': 
    This document requires 'TrustedHTML' assignment.
  3. Click the stack trace link adjacent to the error to highlight the exact JavaScript line performing the unsanitised string assignment.
  4. Open the Sources tab, expand the Breakpoints section in the right sidebar, and tick Trusted Type Violations. Chrome halts execution directly inside the call stack before the DOM sink executes, allowing deep inspection of variables.
  5. To test deployment safety without breaking existing production features, configure your CSP in report-only mode:
    Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri /api/csp-violations;

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.