Herramientas · Texto · Probador de regex · Guías · Catastrophic Backtracking: Why Some Regex Patterns Freeze the Page
Catastrophic Backtracking: Why Some Regex Patterns Freeze the Page
A regex pattern can pass every test you throw at it and still take seconds, minutes, or effectively forever against one specific kind of input - not because anything is broken, but because of how regex engines actually search for a match. This is catastrophic backtracking, and it's a real, well-understood failure mode worth knowing about before a pattern like this ships anywhere it matters.
What backtracking actually is
A regex engine doesn't magically know the right way to match a pattern against text - it tries a possibility, and if that path eventually fails to complete the whole pattern, it backs up and tries a different one, repeating until it either finds a match or exhausts every option. For almost every pattern, this process is fast, because there are only ever a small number of reasonable paths to try. The problem starts when a pattern's structure creates an enormous number of essentially equivalent paths to explore before it can conclude "no match."
Why nested repeating groups are the classic trigger
A pattern like a group with a quantifier, itself wrapped in another quantifier - something in the shape of (a+)+ - against a long string that almost, but not quite, matches, creates a huge number of different ways to split that string across the inner and outer repetition. The engine doesn't know in advance which split will fail, so in the worst case it tries a number of combinations that grows exponentially with the length of the input. A string a little longer than your test case can turn a pattern that resolved instantly into one that would run for an impractically long time.
Why JavaScript can't just cancel a stuck match
This is the part that makes the problem a genuine engineering issue, not just a performance inconvenience: a synchronous regex match running on JavaScript's main thread has no way to be cooperatively interrupted mid-execution. It isn't "checking in" periodically in a way that could be told to stop - it's one long, uninterruptible operation from the browser's point of view. The only real way to stop it is from completely outside that execution context, which in a browser means terminating the entire thread it's running on.
What this tool actually does about it
The real match pass runs in a dedicated background Worker, specifically so it can be forcibly terminated from outside if it runs too long - not for general performance reasons, but because that's the only mechanism that can actually stop a pathological match. A 1-second timeout watches every match attempt; if it's not done by then, the Worker is terminated outright and you get a clear message explaining that the pattern is likely causing catastrophic backtracking, rather than a page that silently stops responding to anything.
How to avoid writing one in the first place
The most common trigger is a repeated group whose own contents can also match in more than one overlapping way against the same text - nested or back-to-back quantifiers over similar character classes are the shape to watch for. Anchoring a pattern tightly (using ^ and $ where you genuinely mean "the whole string"), being as specific as possible in what a repeated group is allowed to match, and avoiding two adjacent quantified groups that could each plausibly absorb the same characters all reduce the number of equivalent paths the engine has to consider.
Testing specifically for this before you ship a pattern
Once a pattern is written, it's worth deliberately testing it against a long string that's designed to almost match but ultimately fail - not just a string that obviously matches or obviously doesn't. If the result comes back quickly, the pattern is very likely safe against real-world input shaped like it. If it hits the timeout instead, you've caught a real problem before it ever reached production, which is exactly what this tool is for.
Why this matters more on a server than in this tool
Here, a stuck pattern costs you one second and a clear message - the Worker gets terminated and the page stays fully responsive the whole time. On a server validating user-submitted input with the same vulnerable pattern, there's often no equivalent safeguard: a single request containing a deliberately crafted or just unusually long string can tie up a request-handling thread for an extended period, and if that happens repeatedly, it becomes a real, practical way to degrade a service - not a hypothetical one. Testing a pattern here first, specifically against adversarial-shaped input, is a genuinely cheap way to catch that risk before it reaches an environment with no timeout guard at all.
A pattern doesn't need to look complicated to be vulnerable
It's tempting to assume only elaborate, expert-looking patterns are at risk, but the opposite is often true - a short pattern with two adjacent quantified groups that can both plausibly match the same characters is enough, and that shape is easy to write by accident while trying to be flexible about optional whitespace or repeated characters. Complexity of the pattern's syntax and vulnerability to catastrophic backtracking are simply different properties; a pattern can be short and dangerous, or long and completely safe.
For the general case of testing a pattern properly, see the background piece and the how-to guide. Or test your own pattern now.