Get listed

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.

ToolBest forWhat to check
DOMPurifyHTML that must renderApache-2.0 ยท sanitizer ยท you call it
Trusted TypesLocking innerHTML, eval, and script URL sinksPlatform policy ยท lock the sink ยท Chromium first
CSP nonce + strict-dynamicStopping injected script. Not a substitute for encoding.HTTP response policy ยท net under the encoder ยท you ship the header
heEncoding HTML text and attributes. Not a sanitizer.MIT ยท encode text ยท you call it
sanitize-htmlServer-side Node allowlistsMIT ยท server allowlist ยท Node
OWASP Java EncoderContextual encoding on the JVMBSD ยท contextual encode ยท JVM
How the tools differ
Text encoders
Encode on the platformNone on this list
HTML sanitizers
Browser locks
1

DOMPurify

Best for HTML that must render

DOMPurify

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.

2

Trusted Types

Best for locking innerHTML, eval, and script URL sinks

Trusted Types

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.

3

CSP nonce + strict-dynamic

Best for stopping injected script. Not a substitute for encoding.

CSP nonce + strict-dynamic

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.

4

he

Best for encoding HTML text and attributes. Not a sanitizer.

he

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.

5

sanitize-html

Best for server-side Node allowlists

sanitize-html

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.

6

OWASP Java Encoder

Best for contextual encoding on the JVM

OWASP Java Encoder

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 questionWhy it mattersWhat to evaluateRed 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.

Application Security resources