Debugging Catastrophic Backtracking

Find out whether a pattern is vulnerable to catastrophic backtracking before it reaches production, using a 500ms worker budget that catches a runaway pattern without freezing your browser.

ZERO UPLOAD · ALL LOCAL
  1. Type a regular expression in the pattern field and toggle the g, i, m, s, and u flags you need. Matching runs automatically as you type.
  2. Paste your test text into the editor below. Every match highlights inline, and each capture group gets its own tint.
  3. Step through matches with the arrows above the inspector to see the full match and every capture group with its exact character range.
  4. Switch to Replace to preview substitutions live, using $1 or $<name> references in the replacement field.
  5. Switch to Unit Tests to pin the pattern down: add test strings, mark each as should match or should not match, and watch the pass count update.
  6. Click Share to copy a link that reproduces your pattern, flags, text, and test cases, or open Explain to read the pattern token by token.

Worked examples for this use case

A classic vulnerable pattern tested against an adversarial input

Before
Pattern: (a+)+$
Test text: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
After
The evaluation hits the 500ms budget and the tool reports a timeout instead of freezing the tab.

Rewriting the pattern to a+ removes the nested quantifier and the timeout disappears on the same input.

Result

  

Debugging Catastrophic Backtracking Before It Ships

A regular expression that works perfectly on every test string you tried can still take down a server the first time someone feeds it a hostile input. Nested or overlapping quantifiers, a pattern like (a+)+, force the matching engine to try an enormous number of ways to split the same text, and that failure mode has a name specific enough to have its own security advisories: catastrophic backtracking.1 Checking a suspicious pattern here, before it reaches production code, is far cheaper than discovering the vulnerability from an incident report.

Why some patterns explode instead of just failing to match

Ordinary pattern failures happen fast. The engine scans the input, finds no valid match, and reports failure in a time roughly proportional to the length of what it scanned. Catastrophic backtracking is different in kind, not just degree, because certain quantifier combinations create exponentially many ways to divide the same string among the pattern's repeating groups.

A pattern like (a+)+$ tested against a long run of the letter "a" followed by one character that breaks the match forces the engine to retry every possible split of that run before it can conclude failure. Security researchers call the attack that exploits this Regular Expression Denial of Service, or ReDoS, and it is a well-documented category with its own entry in the OWASP attack catalog.1 The pattern itself is not malicious; the interaction between its structure and a crafted input is what turns it into a liability.

Watching the timeout guard catch the blow-up

This tester treats that risk as a design requirement rather than an afterthought. The risk is not hypothetical: a single runaway regular expression inside a firewall rule took Cloudflare's entire global network offline for 27 minutes in July 2019, spiking CPU usage to nearly 100% across every server handling HTTP and HTTPS traffic worldwide.2 Every evaluation here runs inside a background Web Worker with a hard 500 millisecond budget, and if the worker has not answered by then, the tool terminates the whole thread and reports a timeout instead of letting your tab freeze.3 That termination is the same signal a vulnerable pattern would give a production server, except here it costs you nothing but a warning banner.

Pasting (a+)+$ against roughly thirty repeated "a" characters followed by one "!" reliably triggers that 500ms cutoff, which is exactly the demonstration you want: proof the pattern is dangerous, produced safely, inside a sandboxed tab instead of a live request handler. Adding or removing a single repeated character from that test string and re-running the pattern shows the cutoff is not a fluke; the timeout appears reliably across a range of input lengths once the underlying structure is genuinely exponential.

Rewriting a vulnerable pattern once you have found one

Finding the vulnerability is only half the job. The fix, in most cases, is removing the nested quantifier structure that creates the ambiguity in the first place, since a single quantifier applied once, rather than a quantifier wrapped inside another quantifier, gives the engine exactly one way to consume each character instead of many.4

(a+)+ becomes a+ when the inner group serves no purpose beyond repeating what the outer group already repeats, and that simplification often preserves the intended matching behavior while eliminating the exponential blow-up entirely.4 Where the nested structure was doing real work, rewriting alternation to be mutually exclusive, so the engine cannot try more than one branch for the same input, achieves the same effect with a more complex pattern.

Proving the rewrite actually removed the risk

Re-testing the rewritten pattern against the same adversarial input, the run of repeated characters that broke the original, confirms the fix actually worked rather than just looking safer on inspection. If the rewritten version returns a result instantly where the original hit the 500ms ceiling, you have direct evidence the exponential path is gone, not just a pattern that reads as more cautious to a human eye.

Keep the original vulnerable pattern and its adversarial test string around somewhere after the fix ships, even in a code comment or a commit message. The next time someone edits that same pattern, re-running it against the exact input that once broke it is a faster regression check than re-deriving from scratch which quantifier combination was dangerous in the first place.

Building a habit of testing before a pattern ships

Nested quantifiers are not always obvious from reading a pattern once, especially inside a larger expression built up over several edits. A pattern that started simple can accumulate a vulnerable structure gradually, one seemingly reasonable addition at a time, until a group inside a group inside a group is quietly waiting for the right input to expose it.

Making adversarial input part of your normal test pass

Treat testing against a deliberately hostile input, a long run of a single repeated character, as a normal part of validating any new pattern before it ships, not an exotic step reserved for security review. It costs one paste and takes under a second to run either way: instant confirmation the pattern is safe, or a clear timeout warning telling you exactly which pattern needs a rewrite before it goes anywhere near a request handler.

That habit is cheap precisely because the tool already isolates the risk for you. Nothing about testing a genuinely dangerous pattern here can affect anything outside the worker thread that gets terminated the moment the budget runs out, which is exactly the isolation a production request handler rarely gets by default.

When to use this

Use this guide any time you need to check a pattern for catastrophic backtracking before it ships, especially one built from nested or overlapping quantifiers.

Examples

A classic vulnerable pattern tested against an adversarial input

Before
Pattern: (a+)+$
Test text: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
After
The evaluation hits the 500ms budget and the tool reports a timeout instead of freezing the tab.

Rewriting the pattern to a+ removes the nested quantifier and the timeout disappears on the same input.

Sources
  1. 1.

    OWASP Foundation, "Regular expression Denial of Service - ReDoS," owasp.org, accessed August 2026. https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS

  2. 2.

    John Graham-Cumming, "Details of the Cloudflare outage on July 2, 2019," blog.cloudflare.com, July 2019. https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019

  3. 3.

    MDN Web Docs, "Worker: terminate() method," developer.mozilla.org, accessed August 2026. https://developer.mozilla.org/en-US/docs/Web/API/Worker/terminate

  4. 4.

    Jan Goyvaerts, "Runaway Regular Expressions: Catastrophic Backtracking," regular-expressions.info, accessed August 2026. https://www.regular-expressions.info/catastrophic.html

FAQ