# CSP & Trusted Types > Block script injection and DOM XSS at the browser's policy layer. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/csp-trusted-types Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary Content Security Policy (CSP) tells the browser which scripts are allowed to run, blocking injected code even when server-side sanitization misses something. Trusted Types closes the remaining DOM XSS surface by requiring that dangerous sinks — innerHTML, eval, script.src — only accept policy-wrapped values, not raw strings. ## Content Security Policy + Trusted Types CSP is an HTTP response header that restricts which scripts, styles, and resources the browser will load. A nonce-based strict policy avoids the fragility of URL allowlists entirely: every inline script that should run carries a cryptographically random nonce the server generates per response. Trusted Types is a complementary browser API that closes DOM XSS — attacks where attacker-controlled strings flow into sinks like innerHTML. With `require-trusted-types-for 'script'`, the browser rejects raw strings at those sinks and only accepts objects produced by a named TrustedTypePolicy. ## Why URL allowlists fail - Any allowlisted origin (CDN, analytics) can host attacker-uploaded scripts. - Wildcard domains like *.example.com let sub-domain takeover bypass the policy. - `unsafe-inline` negates injection protection entirely — it's the default fallback. - Nonce + strict-dynamic avoids the allowlist problem: no URLs needed. - `base-uri 'none'` and `object-src 'none'` close two bypass vectors often missed. ## Nonce CSP header + Trusted Types policy The server mints a nonce per request and stamps it on every legitimate inline script. The browser rejects anything else. **Next.js middleware — nonce CSP + Trusted Types** ```ts // middleware.ts (Next.js 15+) import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { randomBytes } from "node:crypto"; export function middleware(req: NextRequest) { const nonce = randomBytes(16).toString("base64"); const csp = [ `script-src 'nonce-${nonce}' 'strict-dynamic'`, "object-src 'none'", "base-uri 'none'", `require-trusted-types-for 'script'`, `trusted-types default dompurify`, "report-uri /csp-report", ].join("; "); const res = NextResponse.next(); res.headers.set("Content-Security-Policy", csp); res.headers.set("x-nonce", nonce); // pass to layout via header return res; } ``` strict-dynamic propagates the nonce trust to dynamically inserted scripts, so bundlers work without listing every chunk URL. ## Trusted Types policy for safe HTML injection Any code that needs to set innerHTML must go through a named policy. The browser throws a TypeError if raw strings reach the sink. **Trusted Types policy wrapping DOMPurify** ```ts import DOMPurify from "dompurify"; // Create a named policy — 'dompurify' must match the trusted-types directive. const policy = window.trustedTypes!.createPolicy("dompurify", { createHTML: (dirty: string) => DOMPurify.sanitize(dirty), }); // Safe: goes through the policy el.innerHTML = policy.createHTML(userContent); // Unsafe: browser throws TypeError — no raw strings accepted // el.innerHTML = userContent; ← blocked ``` The policy name must appear in the trusted-types CSP directive. Any attempt to bypass it throws synchronously before the DOM is mutated. ## Note: Roll out with report-only first Set `Content-Security-Policy-Report-Only` alongside your existing headers before enforcing. Point `report-uri` at a collector and run for at least one week. Violations reveal inline scripts and sinks you missed; fix them before switching to the enforcing `Content-Security-Policy` header. Trusted Types violations appear in the same report stream under `trusted-types-sink` and `trusted-types-policy` directives. ## Tradeoffs **Pros** - Blocks injected scripts even when input sanitization has a gap. - Nonces eliminate fragile URL allowlists and CDN-hosted bypass risk. - Trusted Types catches DOM XSS at the sink — the last line of defense. - Report-only mode enables incremental rollout with zero user impact. **Cons** - Nonce generation requires server-side per-request work; static CDN serving needs a reverse proxy. - Trusted Types requires migrating every innerHTML, eval, and script.src call — substantial in large codebases. - Browser support isn't universal — verify on Baseline/caniuse and treat Trusted Types as defense-in-depth, not a sole backstop. - A misconfigured policy that allows too-broad sanitization gives false confidence. ## Key terms - **CSP nonce**: A per-response random token that allowlists exactly the inline scripts tagged with it. - **strict-dynamic**: CSP keyword that propagates nonce trust to scripts loaded dynamically by an already-trusted script. - **Trusted Types**: Browser API that enforces type-safe values on dangerous DOM sinks (innerHTML, eval, script.src). - **DOM XSS sink**: A DOM API that executes or injects HTML/JS: innerHTML, eval, document.write, script.src. - **report-only mode**: CSP header variant that reports violations without blocking, safe for gradual rollout. ## Related topics - [XSS, CSRF & Clickjacking](https://fearchitect.com/topics/xss-csrf-clickjacking.md): Three browser-level attacks and the headers that stop them. - [Auth: OAuth, JWT & Sessions](https://fearchitect.com/topics/auth-oauth-jwt-sessions.md): Delegate identity with OAuth, carry claims in JWTs, store state in sessions. - [Supply Chain Security](https://fearchitect.com/topics/supply-chain-security.md): Stop malicious npm packages and third-party scripts from owning your app. - [Rich Text Editor](https://fearchitect.com/topics/rich-text-editor.md): Own a document model; never trust raw contentEditable output. ## Further reading - [MDN — Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) - [web.dev — Mitigate XSS with a strict CSP](https://web.dev/articles/strict-csp) - [web.dev — Prevent DOM-based XSS with Trusted Types](https://web.dev/articles/trusted-types) - [W3C Trusted Types specification](https://w3c.github.io/trusted-types/dist/spec/)