Regex to match or strip HTML tags
s.replace(/<[^>]*>/g, "") removes anything from a < to the next >. That is fine for stripping simple tags from text you control. It is not a parser and not a sanitizer: for untrusted HTML, use DOMParser and read textContent, or a library like DOMPurify.
JavaScript ✓PCRE ✓
No catastrophic-backtracking patterns found. (A heuristic check: nested and adjacent repeats, overlapping alternatives.)
<The character '<' literallyliteral[^>]Any one character except '>'character class*Repeat the class zero or more times, as many as possible (greedy)quantifier>The character '>' literallyliteral
Examples
Checked at build time in JavaScript and in Python's re.
| Input | Matches? |
|---|---|
<p>Hello</p> | ✓ match |
<br/> | ✓ match |
<a href="/x">link</a> | ✓ match |
plain text | ✗ no match |
a < b | ✗ no match |
if a < b and c > d | ✓ match |
<!-- comment --> | ✓ match |
< | ✗ no match |
Try your own text
One string per line: each line is tested on its own (the m flag is added here).
How it works
[^>]* means "anything except >", so each match stops at the first closing bracket. That is what keeps <b>bold</b> from being matched as one long tag, which is what greedy <.*> would do (see greedy vs lazy).
HTML is not a regular language. Tags nest, attribute values can contain >, comments can contain tags, and <script> content has its own rules. A regex sees characters, not structure, so each of those cases produces a wrong result. The examples show some of them.
Variants
/<\/?[a-z][^>]*>/giOnly things that look like real tags: a<, an optional/, then a letter. Skipsa < b, but still matchesa <b and c> d./<!--[\s\S]*?-->/gHTML comments.[\s\S]matches across line breaks, and the lazy*?stops at the first-->.
Gotchas
if a < b and c > dmatches: the regex treats< b and c >as a tag. Any text with less-than and greater-than signs is at risk.- In
<img alt="a>b" src=x>, the match ends at the>inside the attribute and leavesb" src=x>behind. - Stripping tags does not prevent XSS. Input like
<scr<script>ipt>becomes<script>once the inner tag is removed. Use a real sanitizer or escape the output. - Text inside
<script>and<style>survives tag stripping.DOMParserplustextContentalso keeps it, so remove those elements first.
Related explainers
- Greedy vs lazy quantifiers: .* vs .*?How greedy .* and lazy .*? differ, shown on quoted strings, why a negated class like [^"]* is often better...
- Regex for an http or https URLA regex that checks a string is an http or https URL, where it falls short, and why new URL() is the...
- Regex to find repeated words ("the the")Find doubled words like "the the" with a backreference, why word boundaries matter, and the cases where...