Application Security
XSS Defense Tools in 2026: Encoding, CSP, and Sanitizers
Six XSS defense tools and controls: encoders, sanitizers, CSP, and Trusted Types.
Installing a sanitizer and still assigning a string to innerHTML is how XSS tickets come back. Encoding, sanitizing, and locking the sink are 3 different controls.
The category used to mean ‘add DOMPurify’. It now splits: encode text, allowlist HTML only when it must render, then refuse the next raw assignment.
Use the framework encoder first when the output is text. Trusted Types and a tight CSP catch the leftover sink. An edge filter is WAF.
How we evaluated
We read first-party docs, source, licenses, and release notes, and we treat a marketing page as a claim, not as proof two controls do the same work. For XSS we cared about sink coverage, default behavior, framework fit, and whether the defense fails closed.
| Tool | Best for | What to check |
|---|---|---|
| DOMPurify | HTML that must render | Apache-2.0 ยท sanitizer ยท you call it |
| Trusted Types | Locking innerHTML, eval, and script URL sinks | Platform policy ยท lock the sink ยท Chromium first |
| CSP nonce + strict-dynamic | Stopping injected script. Not a substitute for encoding. | HTTP response policy ยท net under the encoder ยท you ship the header |
| he | Encoding HTML text and attributes. Not a sanitizer. | MIT ยท encode text ยท you call it |
| sanitize-html | Server-side Node allowlists | MIT ยท server allowlist ยท Node |
| OWASP Java Encoder | Contextual encoding on the JVM | BSD ยท contextual encode ยท JVM |
DOMPurify
Best for HTML that must render

DOMPurify is the sanitizer most teams already ship when a rich-text field has to become HTML. The README is explicit: it is a sanitizer, not an encoder.
Pair it with Trusted Types if the sink is still innerHTML. The hook API is how you keep the policy tight.
Key features:
- HTML, SVG, and MathML sanitizer for the browser and Node
- Hook API to forbid tags or rewrite URLs
- Documented Trusted Types support
- Default profile strips script and event handlers
Why we like it:
Use it when the product already decided to render HTML. That is a real job. It is not a substitute for encoding text.
Limits:
It cannot save script URLs built from the same string. Server-rendered markup still needs a server sanitizer or encoder.
License or pricing: Apache-2.0.
Trusted Types
Best for locking innerHTML, eval, and script URL sinks

Trusted Types is a browser policy, not a vendor. Once require-trusted-types-for script is on, innerHTML and similar sinks reject raw strings.
A small policy that only your sanitizer can mint is the shape that still holds. The browser does not sanitize for you.
Key features:
- CSP require-trusted-types-for and trusted-types directives
- Blocks string assignment to innerHTML, script src, and eval-like sinks
- Policies return TrustedHTML, TrustedScript, or TrustedScriptURL
- You write the policy. The browser does not sanitize for you
Why we like it:
This is the control that turns a sanitizer from advice into a runtime TypeError.
Limits:
Enforcement landed in Chromium first. A polyfill is not a security boundary in a browser that ignores the policy.
License or pricing: Platform policy.
CSP nonce + strict-dynamic
Best for stopping injected script. Not a substitute for encoding.

A per-response nonce plus strict-dynamic is still the CSP shape that holds. script-src must arrive on the document response.
A meta tag after a script has already run is not a defense. Report-Only first, then enforce.
Key features:
- script-src nonce plus strict-dynamic for scripts the page meant to run
- object-src none and base-uri none as the usual companions
- Report-Only first, then enforce
- Works with Trusted Types on the same Content-Security-Policy header
Why we like it:
CSP is the net. If you cannot nonce the first-party scripts, hashes are the other option that still works.
Limits:
JSONP and old tag managers fight strict-dynamic. unsafe-inline is a comment, not a policy. CSP does not encode output.
License or pricing: HTTP response policy.
he
Best for encoding HTML text and attributes. Not a sanitizer.

Encoding is the first defense when the sink is text. he covers the entities a browser will still parse. It does not sanitize markup.
If you need tags, use a sanitizer. If you need a JVM context, use the Java Encoder.
Key features:
- encode and decode for HTML text and quoted attributes
- useNamedReferences and allowUnsafeSymbols documented in the README
- Works in browsers and Node. No DOM required
- Does not invent a tag allowlist
Why we like it:
Most leftover XSS is a name printed into HTML. An encoder that does not invent an allowlist is the first control worth installing.
Limits:
Wrong context still loses. JavaScript strings, CSS, and script URL attributes are not HTML-text problems.
License or pricing: MIT.
sanitize-html
Best for server-side Node allowlists

sanitize-html is the common Node allowlist when HTML is stored, mailed, or rendered on the server. Name every tag you actually need.
Put it on the write path, not only on the way out. Stored XSS is still the usual miss.
Key features:
- Allowlists for tags, attributes, and schemes
- Transform and exclusiveFilter hooks
- Meant for Node. Not a browser Trusted Types policy
- Used as the server half when the client runs DOMPurify
Why we like it:
The server half when the client already runs a browser sanitizer. That split is the install that matches how the stack is already split.
Limits:
It will not lock innerHTML by itself. An allowlist that includes event-handler attributes rebuilds the bug.
License or pricing: MIT.
OWASP Java Encoder
Best for contextual encoding on the JVM

The Java Encoder is still the checklist item for JSP and server-rendered sinks. Encode for HTML, attributes, JavaScript, and CSS separately.
It is not a sanitizer for rich HTML, and it should not be asked to be one.
Key features:
- forHtml, forHtmlAttribute, forJavaScript, forCssString, and related helpers
- Designed so you pick the context at the sink
- No default clean-this-HTML API, which is the point
- Pairs with the OWASP secure coding checklist
Why we like it:
If the stack is Java and the page is server-rendered, this is the encoder. For rich text, pick a Java HTML sanitizer with an explicit policy.
Limits:
Wrong helper, wrong context. forHtml inside a script tag is not a defense. It will not help a React or Vue sink.
License or pricing: BSD.
How to choose an XSS defense
Four questions before the quote. Names below are tools, not a scored bake-off.
| Critical question | Why it matters | What to evaluate | Red flag |
|---|---|---|---|
| Is this value allowed to become HTML, or is it text? | Encoding is the first control when the sink is text. | Whether he, the OWASP Java Encoder, DOMPurify, or sanitize-html is the named tool. | A sanitizer installed, then a string assigned to innerHTML. |
| Which sink still accepts a raw string after the policy? | Trusted Types only hold if innerHTML cannot take a string. | require-trusted-types-for on the document, and who can mint TrustedHTML. | A polyfill treated as a security boundary. |
| Does the document response carry the CSP? | A meta tag after a script has already run is not a defense. | Nonce or hashes on first-party scripts. Report-Only, then enforce. | unsafe-inline left in as the policy. |
| Do we still encode if CSP is on? | CSP is the net. Encoding is the sink you own. | he or the Java Encoder at text sinks. A sanitizer only if HTML must render. | An edge filter as the only control. |
Sanitizer threads treat the library as a start, not a proof. Encoding is still the first job when the value is text.
Encode first. Sanitize only when HTML must render. Then lock the assignment. The edge filter is a different list.
FAQs
How do CSP, Trusted Types, and a sanitizer share the work?
They are three different jobs. A sanitizer decides which HTML may exist. Trusted Types refuse a raw string at innerHTML, eval, and script URL sinks. CSP decides which scripts the page meant to run.
When do I use he versus the OWASP Java Encoder?
Same job, different runtime. he encodes HTML text and attributes in JavaScript and Node. The OWASP Java Encoder does contextual encoding on the JVM. Neither is a sanitizer.
If I encode every sink, can I skip CSP?
No. Encoding is the primary defense at the sink you own. CSP is the net for the script you did not mean to run. Skip CSP and one forgotten innerHTML becomes a running script.
Is this a scored bake-off?
No. Order is editorial.