ReDoS Checker (Catastrophic Backtracking Detector)

A single badly-written regular expression can freeze a server. Patterns like (a+)+$ take exponentially longer to fail as the input grows — feed one a few dozen non-matching characters and the match can run for minutes. That is a ReDoS (Regular expression Denial of Service). This tool checks a pattern two ways: it statically detects the structures that cause catastrophic backtracking, and it actually measures how match time grows against a hostile input in your own browser, so you get real numbers, not just a warning.

How to use the ReDoS Checker (Catastrophic Backtracking Detector)

Paste the pattern into the Regular expression box. You can enter just the pattern body ((a+)+$) or a full JavaScript literal (/(a+)+$/i) — the tool strips the slashes and reads the flags for you. Add flags such as i or m in the second box if you are not using literal syntax. Click Check for ReDoS.

You get two panels. The first is the static verdict: the tool parses your pattern into a small syntax tree and looks for the three shapes that cause catastrophic backtracking — a nested unbounded quantifier such as (a+)+, an overlapping alternation under a quantifier such as (a|ab)*, and two adjacent quantifiers over the same characters such as \d+\d+. Each finding names the danger and gives a concrete fix.

The second panel is the live timing probe. The tool builds the worst-case input for your pattern — a run of matching characters followed by a character that forces the match to fail — and measures how long the match takes at growing input lengths, right in your browser. A safe pattern stays flat. A vulnerable one climbs steeply, and the probe aborts the moment a single match crosses 750 ms so it can never hang your tab. Everything runs locally; your pattern is never sent anywhere.

What is ReDoS and catastrophic backtracking?

Most regex engines in mainstream languages — JavaScript, Python's re, Java, Ruby, PHP's PCRE, .NET — use a backtracking matcher. When a pattern can match a piece of input in more than one way, the engine tries one option, and if the overall match later fails, it backs up and tries another. For most patterns that is cheap. For a few structural shapes the number of ways to split the input grows exponentially with input length, and the engine tries all of them before giving up. That is catastrophic backtracking, and when an attacker can supply the input, it becomes a denial-of-service vulnerability: ReDoS.

The canonical example is (a+)+$. The inner a+ can match one or more as, and the outer + repeats that, so for an input of n as there are 2n-1 ways to partition them between the two quantifiers. Against "aaaaaaaaaaaaaaaaaaaaaaaaX" the match must fail — there is no X allowed before end-of-string — so the engine explores every one of those partitions first. Twenty-five characters already takes seconds; forty would take longer than the universe has existed. The three structural culprits this tool detects are:

  • Nested quantifiers — an unbounded quantifier wrapping a group that also has an unbounded quantifier: (a+)+, (a*)*, ([a-z]+)*, (\w+\s?)*. Exponential.
  • Overlapping alternation under a quantifier — branches that can match the same character: (a|a)*, (a|ab)*, (\w|\d)+. Exponential.
  • Adjacent overlapping quantifiers — two repeats over the same character set in sequence: \d+\d+, .*.*, \s*\s*. Usually polynomial (quadratic) rather than exponential, but still enough to stall on a long input.

Note that a truly linear engine — Go's RE2, Rust's regex crate, or the non-backtracking option in .NET — cannot suffer ReDoS at all, because it never backtracks. If you control the runtime and the patterns are static, switching engines is the most complete fix. When you cannot switch, the practical defences are: bound every quantifier ({1,64} instead of +), use atomic groups (?>...) or possessive quantifiers a++ to stop the engine reconsidering a run it already consumed, make alternation branches mutually exclusive, and never interpolate untrusted text into a pattern without escaping it.

Common use cases

  • Reviewing a pull request. A new validation regex lands in a diff. Paste it here before approving — nested quantifiers hide easily in an innocent-looking email or URL validator.
  • Auditing a dependency. A CVE mentions a ReDoS in a library's regex. Drop the reported pattern in to see the timing curve and confirm the severity for your own input sizes.
  • Hardening user-facing search or filters. Any feature that compiles a regex from user input, or matches user input against a server-side pattern, is a ReDoS target. Test the pattern against the shape an attacker would send.
  • Learning why a pattern is slow. If a log-parsing or scraping job occasionally hangs on certain lines, the timing probe shows whether the regex is the cause and which structure to rewrite.

Frequently asked questions

Does a "No blow-up detected" result guarantee my regex is safe?

It is a strong signal but not a formal proof. The static analysis covers the three structures behind the overwhelming majority of real ReDoS bugs, and the timing probe measures the classic worst-case input. Very unusual patterns, or ones whose worst case needs a specific non-obvious input, can still slip through. For security-critical code on a backtracking engine, also bound your quantifiers and prefer a linear engine like RE2 or Rust regex.

Why did the static analysis flag my pattern but the timing test stayed fast?

The probe builds one heuristic attack string. Some patterns only explode on a specific input — an overlapping alternation like (a|ab)* needs an "ab" pump, not a run of "a". When the structure is flagged, trust the structure: it is dangerous even if this default probe did not reproduce it.

Is my regex sent to a server?

No. Parsing, analysis, and the timing benchmark all run in your browser with the native RegExp engine. Nothing leaves the page, which is also why the probe reflects the exact engine your users would hit in a browser context.

Which regex flavor does this assume?

The parser and the live benchmark use the JavaScript engine. The dangerous structures — nested quantifiers, overlapping alternation, adjacent overlapping quantifiers — are engine-independent and apply equally to Python, Java, Ruby, PCRE and .NET backtracking engines, so the findings transfer. Exact timings will differ per engine.

How do I actually fix a flagged pattern?

Bound the quantifier ({1,100} instead of + or *), make it possessive (a++) or wrap the group in an atomic group ((?>...)) so the engine cannot backtrack into it, rewrite alternation branches to be mutually exclusive, or collapse adjacent overlapping quantifiers into one. Each finding includes the specific fix for its structure.
Embed this tool on your site

Free to embed, no attribution required (but appreciated). Paste this where you want the tool to appear: