Regex Tester

Test regular expressions against your text in real time with live match highlighting, capture groups, and an explanation of what each part of the pattern does.

When a 26-character string takes a second

The engine built into JavaScript — and Java, Python, PCRE and nearly everything else — does not run a finite automaton. It backtracks: it tries a way of matching, and when that fails it goes back and tries another. Usually fine. Occasionally the number of things to try is exponential in the length of the input.

Running /(a+)+$/ against a run of a's followed by one character that cannot match:

Input lengthTime to fail
20 characters 9 ms
22 characters 40 ms
24 characters 170 ms
26 characters 665 ms

That is a doubling for every single character — roughly four times slower for every two. Extrapolating from the measured 0.665 s at 26 characters: 43 seconds at 32, three hours at 40, and somewhere just past 85 characters it passes the age of the universe. The string is short, there is no nesting depth to speak of, and nothing about the pattern looks wrong.

This is why it has a vulnerability class of its own. A search box, a log parser or an email validator that accepts user input can be stopped dead by twenty-odd characters typed by anyone.

The shape to watch for, and the fix

The trigger is always the same: a quantifier wrapped around something that is itself quantified, where the inner part can match the same text more than one way. For a run of n a's, (a+)+ has 2n−1 ways to carve it into groups, and a failing match tries every one.

DangerousSafeWhy
(a+)+$ a+$ the outer group adds nothing — one way to match instead of 2^n
^(\w+\s?)*$ ^[\w\s]*$ a character class cannot be carved up ambiguously
(.*)*x .*x the inner star already reaches everywhere the outer one could

Each rewrite matches exactly the same strings and runs in under a millisecond at the length where the original takes hundreds. The danger comes from ambiguity rather than complexity, so the repair is almost never clever — it is removing a group that was doing nothing, or replacing an alternation with a character class.

Rule of thumb: if a group containing + or * is itself followed by + or *, check whether the inner part can match the same characters two different ways. If it can, that pattern is waiting for the wrong input. There are 4 worked examples of the shape in the tests behind this page.

How to use

  1. Enter your pattern and the text to test against.
  2. Matches highlight as you type.
  3. Inspect the capture groups for each match.
  4. Add flags for global, case-insensitive or multiline matching.

Frequently asked questions

What is the difference between greedy and lazy matching?

Greedy quantifiers take as much as possible and then give back; lazy ones take as little as possible and then extend. Matching a tag with angle brackets greedily will swallow everything to the last bracket on the line, which is the single most common surprise for people new to regular expressions. Adding a question mark after the quantifier makes it lazy.

Why is my pattern so slow?

Probably catastrophic backtracking. Nested quantifiers over overlapping alternatives can force the engine to try an exponential number of paths, so a pattern that runs instantly on short input hangs on a slightly longer one. It is a real denial-of-service vector, and the fix is usually to make the alternatives mutually exclusive.

Should I use a regular expression to parse HTML?

No. HTML nests arbitrarily and regular expressions cannot express arbitrary nesting, so any pattern that appears to work is relying on the input being well behaved. Use a proper parser. The same applies to JSON, XML and most other structured formats.

What does the global flag actually change?

It makes the engine find every match rather than stopping at the first. In JavaScript it also makes the expression object stateful, remembering where it left off between calls — which produces the notorious bug where testing the same string twice alternates between true and false.

How do I match a literal dot or bracket?

Escape it with a backslash. Unescaped, a dot matches any character, and brackets and parentheses have structural meanings. Inside a character class most of these lose their special meaning, so a dot inside square brackets is already literal.

Do all regular expression engines behave the same?

No. JavaScript, Python, PCRE, Go and POSIX differ in lookbehind support, named group syntax, Unicode handling and more. A pattern that works in one may fail or behave differently in another, so test in the engine you will actually run.

🔒 This tool runs entirely in your browser. Nothing you enter is uploaded, logged, or stored.