TexToolsAI

sanitize-html vs DOMPurify: Which HTML Sanitizer Should You Use?

By VeloForge Labs · · 3 min read

If you render user-written Markdown or rich text, you need an HTML sanitizer. Two libraries come up in almost every discussion: DOMPurify and sanitize-html. Both are popular and both work. The right choice depends mostly on where your code runs.

The short version

  • DOMPurify leans on the browser's own HTML parser. It is a great fit for client-side code, and it can run on a server if you provide a DOM implementation such as jsdom.
  • sanitize-html is built for Node.js. It parses HTML with htmlparser2, needs no DOM, and is configured with a straightforward allow-list.

How they work

DOMPurify parses the dirty HTML into a real DOM tree, walks the nodes, removes anything not allowed, and serializes the result. Because it uses the browser's parser, it sees markup roughly the way the browser will. It was written by the Cure53 security team with mutation XSS (mXSS) in mind, where the browser re-parses sanitized markup into something different than what the sanitizer saw.

sanitize-html parses the input with htmlparser2, a fast streaming parser, and rebuilds the output from the tags and attributes your configuration allows. It never constructs a DOM, which is why it runs anywhere JavaScript runs on the server.

Runtime support

DOMPurify sanitize-html
Browser Native Possible, but not its main use
Node.js Needs jsdom or similar Works directly
Edge and serverless runtimes without a DOM Awkward Generally easier

If you run on a platform without a DOM, such as many edge runtimes, DOMPurify needs extra work. sanitize-html avoids the DOM dependency.

Configuration style

sanitize-html expresses policy as plain options: allowed tags, allowed attributes per tag, allowed URL schemes. That makes the policy easy to read and review:

import sanitizeHtml from "sanitize-html";

const clean = sanitizeHtml(dirtyHtml, {
  allowedTags: ["p", "h1", "h2", "a", "img", "code", "pre", "table"],
  allowedAttributes: {
    a: ["href", "title"],
    img: ["src", "alt"],
  },
  allowedSchemes: ["http", "https", "mailto"],
});

DOMPurify has sensible defaults and options such as ALLOWED_TAGS and ALLOWED_ATTR, plus a hooks system for custom logic.

import DOMPurify from "dompurify";

const clean = DOMPurify.sanitize(dirtyHtml, {
  ALLOWED_TAGS: ["p", "h1", "h2", "a", "img", "code", "pre", "table"],
  ALLOWED_ATTR: ["href", "title", "src", "alt"],
});

Security considerations

Both libraries are maintained and widely deployed, and both have had security advisories fixed over the years, which is normal for software in this space. A few practical rules apply to either:

  • Keep the library up to date.
  • Sanitize on the server for stored content, and again on the client if you inject HTML into the DOM dynamically.
  • Use an allow-list that is as small as your product needs.
  • Sanitize the final HTML, after Markdown conversion.

A parser that differs from the browser's can in theory disagree about how some malformed markup is structured. That is the reason DOMPurify is favored for browser-side sanitizing and the reason a tight allow-list matters on the server.

So which one?

Pick DOMPurify if you sanitize in the browser, or already run jsdom on your server. Pick sanitize-html if you sanitize in plain Node.js or an edge runtime and want a readable, allow-list-style configuration.

Or skip the decision. TexToolsAI uses sanitize-html behind a single endpoint, so your Python, Go, or Node service gets sanitized HTML without installing either library. The overall approach is covered in Why Markdown is not safe by default.