Skip to content

Regex Tester With Backtracking Warning

Test a pattern against your own text with a step budget, and find out before production whether it can be made to backtrack catastrophically.

Everything on this page runs in your browser. Nothing you paste is uploaded, logged, or written into the URL — only the settings are, so a configured tool can still be linked to. The pattern, the flags and the budget are settings and do travel in the link; the subject you test against does not.

Flags
Backtracking steps
over 200,001

The budget of 200,000 steps was exhausted without an answer. Your browser's engine was never handed this pattern.

subject lengthstepsresult
4 characters89no match
6 characters163no match
9 characters782no match
14 characters6,867no match
21 characters138,458no match
32 charactersover 200,001gave up
44 charactersover 200,001gave up

The same pattern against the first N characters of your subject, with its final character kept on the end so a rejecting case stays a rejecting case. Steps roughly doubling for a handful of extra characters is catastrophic backtracking; steps growing in step with the length is a healthy pattern.

  • errorpattern offset 9a quantifier wrapped around something that is itself quantified — the (a+)+ shape. The number of ways to divide a run of matching characters between the inner and outer repeats is exponential in the length of that run, so a subject that ALMOST matches takes exponential time to reject

    Expected: a single quantifier over the repeated unit — (a+) or a+ — or an inner unit that cannot match what the outer one can. This is the shape behind essentially every published regular-expression denial of service

    ^(\w+\s?)*$
  • error200,001 stepsthe match was abandoned at the step budget. Nothing was handed to your browser's engine, because a search this large is exactly the one that would not come back

    Expected: a pattern that resolves within the budget. Raise the budget to see how far it gets, or read the growth table below — if the steps double for every character added, no budget is large enough

  • errorthe step count grows as roughly length^0.0 across the last two rows of the growth table — steeper than quadratic, which is the signature of exponential backtracking rather than a merely slow pattern

    Expected: steps growing about linearly with the subject. Adding ten characters should cost ten times one character, not a thousand times

What this checked: this parses your pattern, reports the offset and the expectation for any syntax error, runs it against your subject with a matcher that counts and caps every step, measures how the step count grows with subject length, and inspects the parse tree for the three shapes that cause catastrophic backtracking: a quantifier around something already quantified, a repeated alternation whose branches can start with the same character, and two overlapping unbounded quantifiers in a row. The matches and capture groups shown above come from your browser's own engine, which is authoritative — but it is only asked once the budgeted matcher has already come back. It does not implement lookaround, backreferences, named groups, \p escapes or the u, v, y and d flags; a pattern using any of them is reported and not executed at all, rather than analysed by halves. And a clean report is not a proof: this checks the shapes that are known to explode, not every pattern that could.
What this assumes: the matcher works on UTF-16 code units, like a JavaScript regular expression without the u flag, so an emoji is two units and a dot matches half of one. Step counts are this matcher's, not V8's: a real engine has optimisations — a literal prefix scan, a memoised one-character lookahead — that cut the constant factor by a large amount. What transfers is the shape of the growth, which is a property of the pattern rather than of the engine, and it is the shape that decides whether a 40-character input takes a microsecond or a fortnight. Subjects are capped at 2,000 characters and patterns at 400.

Why the failing case is the expensive one

A regular expression that matches usually stops at the first success. One that fails has to prove no arrangement works, and in a backtracking engine that means trying every arrangement. Wrap a quantifier around something already quantified and the number of arrangements doubles for each extra character: (a+)+$ against thirty a's and an exclamation mark is a billion attempts. That is the whole attack. An input field, a log line or a filename that a user controls, matched against a pattern with this shape, is a single request that pins a CPU core for minutes.

What to do when the warning fires

Almost every nested quantifier is an accident of writing rather than an intent. (\w+\s?)* was meant to say "words separated by optional spaces", and \w+(\s\w+)* says the same thing with no ambiguity at all, because a space is the only way into the repeat. The general fix is to make each repetition start with something only it can consume. Where the pattern genuinely has to be ambiguous, or where it comes from a user, the answer is not a cleverer pattern: it is a length cap on the input, a regular-expression engine with a linear guarantee, or moving the work off the request thread. Timeouts do not help — there is nothing to interrupt.

Regex Tester With Backtracking Warning · Multigrid