Regex Tester
Local processing · verifiedTest a regular expression with live highlighting and capture groups.
Matching runs in your browser. Log lines and sample data stay local. How to verify this yourself.
Pattern library
Catastrophic backtracking, and the three patterns that cause it
This page runs your pattern against your text with the browser's own regular expression engine. It is explicit about that for a reason: the engine is ECMAScript, not PCRE, and the two are not interchangeable. Everything happens in the tab, so the log lines or payload you paste are never uploaded. The sections below cover the failure mode that regex testers exist to expose — runaway backtracking — and the places where JavaScript syntax differs from what you may have written on the server.
What catastrophic backtracking actually is
Most regular expression engines, JavaScript's included, are backtracking matchers. When a pattern can match the same
text in more than one way, the engine tries one arrangement, and if the rest of the pattern fails it unwinds and
tries the next. That is fine when the number of arrangements is small, and ruinous when it is exponential. The
classic example is (a+)+ tested against a long run of as that does not end the way the
pattern needs, for instance twenty as followed by a b. Each a can be claimed
by the inner quantifier or by the outer one, so there are roughly two ways to divide every character, and the
engine explores all of them. At twenty characters that is a million paths; add ten more and it is a billion. The
tab stops responding, and because the work happens synchronously there is no way to cancel it.
This page looks for that shape before it runs the pattern and warns you. It is a heuristic, not a proof: a pattern it accepts can still backtrack badly, so treat a clean result as "nothing obvious" rather than "safe".
The three shapes that cause it, and the usual fix
The first shape is a quantified group inside a quantified group: (a+)+, (\d+)*,
([a-z]+){2,}. The second is a quantified group whose alternatives overlap, as in
(a|aa)+ or (\w|\d)+, where the engine has several ways to consume each character. The
third is a quantified group that can match the empty string, such as (a*)* or (?:,|)*, so
the loop can iterate forever without consuming input. All three share one cause: a group that can match the same
text in multiple partitions.
The fix is almost always to make each character matchable in exactly one way. Replace a broad inner repetition with
a negated character class that stops at the first character you care about: instead of
(.*) inside another repetition, use ([^"]*) when the field ends at a quote. Anchor the
pattern so a failure is discovered early rather than after every split, and prefer [^,] over
.*? when you only want "up to the next comma". JavaScript has no atomic groups and no possessive
quantifiers, so unlike in PCRE you cannot write (?>a+) or a++ to forbid backtracking;
restructuring the pattern is the only option. If a pattern has to accept untrusted input, also cap the length of
that input, because that is the one control that always works.
JavaScript regex is not PCRE
Several constructs a server-side developer reaches for simply do not exist in ECMAScript. There are no atomic
groups (?>…), no possessive quantifiers such as a*+, no \K to reset the match
start, no subroutine calls or recursion (?R), no branch-reset groups (?|…), and no
\A, \Z or \h shorthands. Anchoring is done with ^ and
$ plus the m flag, Unicode properties such as \p{Letter} require the
u flag, and named capture groups use (?<name>…). Lookbehind works in current
engines but is not universally available, so a pattern relying on it can fail on older runtimes. None of this is a
defect in the engine — it is a different specification, and a pattern that passes here will behave the same way in
your browser code and in Node, but not necessarily in a PCRE-based server.
Flags change the meaning of the pattern, not just the search
The g flag makes a regex stateful: it stores a lastIndex and continues from there on the
next call. In application code that is the source of a notorious bug — calling test() with a
g regex on the same string repeatedly returns true, false,
true, false, because the position walks forward each time. Here, g is what
lets the tool collect every match rather than stopping at the first. The m flag makes ^
and $ match at line boundaries instead of only the start and end of the whole text, which is what you
want for a multi-line log. The s flag lets the dot match a newline, off by default. And the
u flag switches to code-point semantics, so a pattern like . consumes a whole emoji
instead of one half of its surrogate pair. Toggling these on this page changes the result immediately, which is the
fastest way to see why a pattern behaved differently in two places.
How to verify that nothing is uploaded
Open the network panel, clear it, and then run a pattern over some text. There is no request. Take the network offline and run it again: still works, because the match is computed in the page. The only third-party script is the advertising tag, which cannot read the workbench. The privacy policy lists every script the page loads.
Common questions
- Which regex engine does this run?
The JavaScript engine only — the same RegExp implementation your browser ships, which follows ECMAScript, not PCRE. Syntax that exists in PCRE but not ECMAScript, such as atomic groups (?>…), possessive quantifiers, \K and subroutine calls, will not work here. The page says so on purpose rather than silently accepting a pattern that behaves differently on your server.
- Why does nothing match even though the pattern looks right?
Check the flags first. A pattern with anchors and no m flag only matches at the very start and end of the whole text, so ^Error$ finds nothing in a multi-line log. Likewise, a character class containing a literal dot matches a dot, while an unescaped . matches any character. The flags row here toggles the same flags the engine uses, so you can confirm the behaviour without leaving the page.
- The page warned about catastrophic backtracking. Should I worry?
Yes, if the pattern will ever run on input you do not control. Patterns like (a+)+ or (a|aa)+ can take exponential time on a string that almost matches, and in a browser that means a frozen tab with no way to cancel. The warning is a static hint, so its absence does not prove a pattern is safe — always bound the input length when a user-supplied pattern meets user-supplied text.
- Why is the match list capped?
A pattern such as \d matches every digit in the text, so a megabyte log would produce hundreds of thousands of results and lock the tab while the list is built. The tool collects at most 1000 matches and tells you when it stopped early. Narrow the pattern or shorten the text if you need the rest.
- Does my test text leave the browser?
No. Matching happens with the built-in RegExp object in the page. Log lines and sample data are often the most sensitive thing a developer pastes into a tool, which is why none of it is sent anywhere. Watch the network panel, or use the tool offline, to confirm.