Regex for HTML Tag Stripping — and Why It Is Not Safe for Untrusted Input

Stripping HTML tags with regex is one of the most common "quick fix" snippets developers reach for — generating a plain-text preview, trimming a description for a meta tag, or cleaning up copy-pasted content. It works fine for trusted, well-formed HTML, but it is a genuine security risk when the input comes from users. This guide shows both sides.

Basic Tag Stripping

The simplest approach removes anything between < and >:

function stripTags(html) {
  return html.replace(/<[^>]*>/g, '');
}

stripTags('<p>Hello <strong>world</strong>!</p>');
// 'Hello world!'

Stripping Tags but Keeping Text Spacing

Block-level tags like <p> and <div> should leave a space or line break behind, or words from adjacent elements will run together:

function stripTagsWithSpacing(html) {
  return html
    .replace(/<\/(p|div|li|h[1-6]|br)>/gi, ' ')  // add space after block-level closing tags
    .replace(/<[^>]*>/g, '')                       // strip remaining tags
    .replace(/\s+/g, ' ')                          // collapse extra whitespace
    .trim();
}

stripTagsWithSpacing('<div>Hello</div><div>World</div>');
// 'Hello World'  (not 'HelloWorld')

Where This Breaks Down

The naive <[^>]*> pattern assumes tags are well-formed and never contain a > inside an attribute value. Real-world (or adversarial) HTML frequently violates that assumption:

// A > inside an attribute breaks the naive regex
const tricky = '<img src="x" onerror="alert(1)" title="1 > 0">';
tricky.replace(/<[^>]*>/g, '');
// Result may leave a fragment behind or match incorrectly depending on nesting

// Script tags without a clean closing structure
const nested = '<scr<script>ipt>alert(1)</scr</script>ipt>';
// Regex-based filters historically failed on nested/broken tags like this,
// letting the payload survive the "sanitization" step

This is exactly how many historical XSS filter bypasses worked: an attacker crafts input that looks stripped to a regex but reassembles into an executable tag once the browser's real HTML parser gets hold of it. Regex has no concept of nesting, attribute quoting, or parser state — it is fundamentally the wrong tool for security-sensitive HTML handling.

The Safe Alternative — Real Parsers

For anything user-submitted (comments, bios, rich-text editor output), use a library that actually parses HTML into a tree and applies an allow-list:

// Node.js — using sanitize-html
const sanitizeHtml = require('sanitize-html');

const clean = sanitizeHtml(userInput, {
  allowedTags: [],       // strip all tags — plain text only
  allowedAttributes: {},
});

// Browser — using the DOM itself for text extraction (safe, no execution)
function stripTagsSafely(html) {
  const doc = new DOMParser().parseFromString(html, 'text/html');
  return doc.body.textContent || '';
}

DOMParser parses the string exactly like a browser would when rendering it, so textContent reliably reflects only the text nodes — no script execution, no partial-tag leakage.

When Regex Stripping Is Actually Fine

  • The HTML source is fully trusted — e.g. generated by your own CMS or markdown renderer, never user-submitted
  • You only need a rough plain-text preview (like a search result snippet) where perfect correctness doesn't matter
  • The output is never re-rendered as HTML anywhere — it is stored/displayed strictly as plain text
  • For anything else — comments, chat messages, profile bios, uploaded content — use a real parser or sanitizer

Frequently Asked Questions

What regex strips HTML tags from a string?

A simple pattern is /<[^>]*>/g used with String.replace to remove anything between angle brackets. It works for quick plain-text previews of trusted, well-formed HTML.

Is it safe to use regex to sanitize untrusted HTML input?

No. Regex-based tag stripping can be bypassed with malformed or nested tags and does not understand HTML parsing rules, which can leave XSS vectors intact. Use a dedicated sanitization library like DOMPurify, or parse with the DOM/an HTML parser, for any untrusted input.

What should I use instead of regex to safely remove HTML in Node.js?

Use a library such as sanitize-html or DOMPurify (with jsdom on the server) which parses the HTML into a real DOM tree and removes disallowed tags and attributes based on an allow-list, rather than pattern-matching text.

Try the Free AI Regex Generator

Describe any validation rule in plain English and get a working regex instantly — no signup required.

Related articles