When you genuinely need to render user-supplied HTML (like formatted rich-text comments), sanitization strips dangerous elements and attributes while preserving safe formatting โ a fundamentally different (and more robust) approach than trying to hand-write your own escaping logic.
1HTML Sanitization | JavaScript Tutorial - In-Depth Guide Part 1
Sanitization parses untrusted HTML and removes anything dangerous (script tags, event handler attributes, javascript: URLs) while preserving safe formatting elements like <b>, <i>, and <a>.
const dirty = '<p>Hello <script>evil()</script><b>world</b></p>';
const clean = sanitize(dirty);
// '<p>Hello <b>world</b></p>' โ script removed, safe formatting keptSafely Allowing Rich Content
2HTML Sanitization | JavaScript Tutorial - In-Depth Guide Part 2
Never attempt to write your own HTML sanitizer using regular expressions โ HTML parsing has enormous edge cases and encoding tricks that make regex-based filtering reliably bypassable by a determined attacker.
// DON'T do this โ regex-based sanitization is notoriously bypassable:
const broken = html.replace(/<script.*?<\/script>/gi, '');
// Easily bypassed with e.g. <scr<script>ipt> or event handler attributesNever Hand-Roll Sanitization with Regex
3HTML Sanitization | JavaScript Tutorial - In-Depth Guide Part 3
An established sanitization library (like DOMPurify) parses the HTML with a real HTML parser and removes anything not on an explicit allow-list of safe tags and attributes.
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userSuppliedHtml);
element.innerHTML = clean; // now safe to insertUsing an Established Library
4HTML Sanitization | JavaScript Tutorial - In-Depth Guide Part 4
Configure the sanitizer with an explicit allow-list of exactly which tags and attributes are permitted for your specific use case โ the more restrictive the allow-list, the smaller the attack surface.
const clean = DOMPurify.sanitize(userHtml, {
ALLOWED_TAGS: ['b', 'i', 'a', 'p'],
ALLOWED_ATTR: ['href'],
});Configuring an Allow-List
5HTML Sanitization | JavaScript Tutorial - In-Depth Guide Part 5
Sanitize on the SERVER before storing user content whenever possible, in addition to (not instead of) sanitizing again on the client before rendering โ defense in depth against any single point of failure.
// Server: sanitize before storing
const safeToStore = sanitizeOnServer(userSubmittedHtml);
db.save({ comment: safeToStore });
// Client: sanitize again before rendering, as defense in depth
el.innerHTML = DOMPurify.sanitize(fetchedComment);Defense in Depth
6Step-by-Step Breakdown
Sanitization parses untrusted HTML and removes anything dangerous (script tags, event handler attributes, javascript: URLs) while preserving safe formatting elements like <b>, <i>, and <a>.
Never attempt to write your own HTML sanitizer using regular expressions โ HTML parsing has enormous edge cases and encoding tricks that make regex-based filtering reliably bypassable by a determined attacker.
Checkpoint: Is a hand-written regex-based HTML filter a reliable way to prevent XSS?
- โYes, regex can reliably catch all dangerous HTML
- โNo, HTML parsing edge cases make regex-based filtering bypassable
An established sanitization library (like DOMPurify) parses the HTML with a real HTML parser and removes anything not on an explicit allow-list of safe tags and attributes.
Configure the sanitizer with an explicit allow-list of exactly which tags and attributes are permitted for your specific use case โ the more restrictive the allow-list, the smaller the attack surface.
Sanitize on the SERVER before storing user content whenever possible, in addition to (not instead of) sanitizing again on the client before rendering โ defense in depth against any single point of failure.
Checkpoint: Is client-side-only sanitization (with no server-side sanitization) sufficient defense in depth?
- โYes, client-side sanitization alone is always enough
- โNo, other consumers of the stored data may not re-sanitize
Next, we'll explore 'Content Security Policy Basics'.
Level Up ๐
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1A Well-Configured Sanitizer Preserves Semantic HTML Needed for Accessibility
Allowing genuinely meaningful structural tags (like <strong>, <em>, and properly-attributed <a>) through the sanitizer, rather than stripping all HTML down to plain text, preserves the semantic cues assistive technology relies on to convey emphasis and interactivity correctly.
SEO Implications
- 1
Sanitization Prevents Spam/Malware Injection into Indexed User-Generated Content
Properly sanitizing user-generated content (comments, reviews, forum posts) prevents attackers from injecting hidden spam links or malicious redirects that search engines could penalize the site for, protecting the domain's search reputation.
Best Practices
Always Use an Established, Actively-Maintained Sanitization Library
Real HTML parsing and a continuously-updated understanding of new bypass techniques are things only a dedicated, battle-tested library can reliably provide.
Configure the Tightest Allow-List Your Actual Use Case Requires
Only permitting the specific tags and attributes your feature genuinely needs (rather than a broad default) minimizes the residual attack surface even after sanitization.
Frequent Bugs
Writing a custom regex to strip <script> tags, which an attacker bypasses using an alternative XSS vector like an `<img onerror=...>` tag or encoded characters the regex doesn't account for.
Replace any custom regex-based filtering with an established sanitization library that uses a real HTML parser and allow-list.
Sanitizing user content only when it is first submitted, but never re-sanitizing when rendering it later, missing protection if the storage layer itself is ever compromised or bypassed by another code path.
Sanitize both when storing data and again immediately before rendering it, as independent, defense-in-depth layers.
Real-World Examples
Allowing Rich-Text Formatting in Blog Comments
A blog wanted to let commenters use basic formatting (bold, italic, links) without exposing the site to script injection.
import DOMPurify from 'dompurify';
function renderComment(rawHtml) {
const clean = DOMPurify.sanitize(rawHtml, {
ALLOWED_TAGS: ['b', 'i', 'a'],
ALLOWED_ATTR: ['href'],
});
commentEl.innerHTML = clean;
}