BV
All tools
developer

Regex Tester

Write and test regular expressions against your own sample data with live match highlighting and separated capture groups, plus the backtracking pitfalls that turn a working pattern into a hung process.

Muhammad Bilal
Muhammad Bilal Virk
8 min read
Live tool
Matches
2
Capture groups
2
hello jane@acme.com and bob@corp.com
#MatchIndexGroups
1jane@acme.com6jane | acme
2bob@corp.com24bob | corp

Paste a pattern, paste your sample text, and watch the matches light up as you type. Capture groups are listed separately so you can see exactly what your workflow will receive. Everything runs in your browser, so real payloads never leave the machine.

What This Tool Does

Paste a pattern, paste the text you expect it to match, and every match lights up as you type with capture groups listed separately. It runs in your browser on JavaScript's own RegExp engine, so a real webhook payload with a customer's email address in it never leaves your machine.

It is built for the moment you are halfway through an automation and need one field out of a blob of text: an order number from an email subject, a reference from a log line, a postcode from a form submission. Those are the jobs regular expressions are genuinely good at. Getting the pattern right here, against real sample data, is cheaper than discovering at run time that it matched very nearly the right thing on every third record.

Regex Tester — illustration

How to Read the Output

The panel shows two different things, and confusing them is the most common reason a pattern that works in a tester breaks in a live workflow.

Take one line from a shipping notification:

text
Order #10482 shipped on 14 August, ref #77 pending

The pattern Order #(\d{4,}) produces one match. The full match is Order #10482. Group 1 is 10482. Those are not the same string, and which one your automation receives depends entirely on how you reference it: $0 or match[0] hands you the full match, $1 or match[1] hands you the group. Feed Order #10482 into a lookup expecting an integer and it fails on every record, quietly, while the tester sits there telling you the pattern matched.

Now loosen it to #(\d+) and you get two matches: #10482 and #77. The second is the internal reference, not the order. That {4,} was doing real work. It said "at least four digits", and that single constraint was the only thing separating the number you wanted from the number you did not. So when you widen a pattern to make one stubborn record match, read the match count, not just the first result.

The Number No Regex Tester Shows You

Every tester tells you whether a pattern matched. Almost none tell you how long it took, and that is the number that takes production down.

Here is ^(a+)+$ tested against a string of a characters followed by a single !, so the match always fails at the last character. Timings measured with CPython's re module on one ordinary machine, so treat the absolute values as indicative and the shape of the curve as the point:

Input length Time to fail
18 characters 6.3 ms
20 characters 25.3 ms
22 characters 105.0 ms
24 characters 416.4 ms
26 characters 1,643 ms
28 characters 6,674 ms

Each extra character roughly doubles the work. At 28 characters it is already nearly seven seconds. Ten characters further on, at 38, the same curve puts it somewhere around two hours. For comparison, ^a+$ against 28 characters takes 1.3 microseconds, and against 100,000 characters takes 379.8 microseconds. That is linear, and it is the behaviour you assumed you had.

The academic example is easy to dismiss, so here is the same trap in a pattern people actually write. ^([a-zA-Z0-9._%-]+)+@example\.com$ is a plausible-looking email check. Against 22 a characters followed by @example.co (note the missing m, so the match fails at the end) it took 192,820 microseconds, a fifth of a second. At 24 characters, 783,530 microseconds, or 0.78 seconds. The identical pattern with the redundant group and its + removed, ^[a-zA-Z0-9._%-]+@example\.com$, ran against the same input in 4.4 microseconds. Roughly 180,000 times faster, from deleting two characters that appeared to do nothing.

If that pattern sits in an API endpoint, a form validator or an n8n Function node, then a 24-character address is not a bad input. It is a denial-of-service request that costs the sender nothing.

Why It Happens

JavaScript, Python, PHP and Java all use backtracking engines. The engine tries one way of satisfying the pattern, and if that route fails it walks back to the last decision point and tries the next option. Nest one unbounded quantifier inside another and the number of decision points stops growing with the input and starts growing with two to the power of the input, because there are that many ways to split a run of a characters into groups of one or more. The engine has to try all of them before it can honestly report no match.

That is also why the trigger is a near miss rather than a mismatch. A string that fails early fails fast. A string that satisfies the whole pattern except the last character forces the engine to exhaust every alternative.

The fix is not to write shorter patterns, it is to avoid nesting unbounded quantifiers and to bound the ones you can. The HTML specification's own email validation regex, reproduced by MDN as the algorithm browsers use for type="email", looks far more frightening than the naive version above:

text
/^[\w.!#$%&'*+/=?^`{|}~-]+@[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?(?:\.[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?)*$/i

Fed deliberately awkward input, 40 a characters followed by 40 repetitions of a. and a trailing !, it completed in 4.4 microseconds. A valid address takes 0.4 microseconds. A thousand a characters before the @ takes 3.0 microseconds. Nothing unbounded is nested inside anything unbounded, and every repeat inside the domain half is capped at {0,61}, which with the two mandatory characters either side comes to 63, the maximum length of a DNS label. Constraints that look like clutter are the reason it is safe.

Which Engine You Are Actually Running

Engine Design Backreferences and lookaround Worst case
JavaScript RegExp (browsers, Node) Backtracking Supported Exponential
Python re Backtracking Supported Exponential
PCRE (PHP, nginx, many CLI tools) Backtracking Supported Exponential
RE2, Go regexp, Rust regex Alternatives evaluated in parallel Not supported Linear in input length

Google's RE2, in production since 2006, states the trade plainly: safety is the primary goal, match time is guaranteed linear in the length of the input, and backreferences and lookaround are therefore not supported at all. Go's standard library and Rust's regex crate follow the same principles. This tester runs on your browser's backtracking engine, so a pattern that behaves here behaves the same way in Node and in an n8n JavaScript step, and tells you nothing reliable about Go.

Common Mistakes

Nesting one unbounded quantifier inside another. (a+)+, (\d*)*, ([\w-]+)* and anything of that shape. The outer quantifier almost never adds meaning, because the inner one already matched as much as it could. Delete it.

Using a regex to validate an email address at all. MDN is explicit that browser-side email validation "must not" be used for any security purpose and that the address has to be verified on the server. The only test that proves an address exists is sending something to it and seeing what comes back. A pattern can reject obvious typos, and that is the whole of its job.

Running a user-supplied pattern against user-supplied input. If either side comes from outside your system, a backtracking engine gives an attacker a way to burn your CPU with a short request. Put a length cap on both, run it somewhere you can kill it, or use an engine with a linear-time guarantee.

Frequently Asked Questions

Why does my pattern work here but not in n8n or Make.com?

Usually escaping. Both platforms pass patterns through a JSON field, so a backslash has to survive twice, and \d written directly into some expression fields arrives at the engine as d. Test the pattern here first so you know the pattern itself is sound, then treat any remaining difference as an escaping problem rather than a regex problem.

What is catastrophic backtracking?

A pattern where the engine has to try an exponential number of ways to match before it can conclude that it cannot. It needs a nested unbounded quantifier and an input that almost matches. The result is a request that appears to hang, with a CPU core pinned, and no error in the log. The measurements above show it going from six milliseconds to nearly seven seconds across ten added characters.

Should I use a regex to validate an email address?

Only for catching typos, and if you do, use the specification's pattern rather than writing your own. Even that one rejects some technically valid addresses, and MDN notes ongoing spec issues with international domain names. Verify by sending mail.

Taking a Pattern Into Production

A tester tells you whether a pattern matches the sample you pasted. It cannot tell you what happens when the field is missing entirely, when the same webhook fires twice, when the payload arrives with different key ordering, or when the upstream service changes its subject line and every match silently returns nothing. Those failures do not look like regex failures. They look like a workflow that used to work.

More often than not, a pattern that keeps needing another special case is a sign the parsing should not be a pattern at all. If the data has structure, read the structure: check the JSON with the JSON formatter before you write a pattern against it, verify a webhook signature with the hash generator rather than pattern-matching a header, and see the Make.com webhook tutorial for how much of this a properly parsed payload removes. When the volume justifies it, the durable answer is a small service in front of the mess, which is what the Python FastAPI webhook guide walks through.

That is the sort of work I do: taking automations that mostly work and making them fail loudly instead of quietly, whether that means retries and error branches in n8n, a FastAPI service in front of a flaky endpoint, or replacing three brittle patterns with one proper parser. If you have got a pattern right and the thing around it is what keeps breaking, email me at iam.mbilalvirk@gmail.com with what is going wrong.

Muhammad Bilal
Muhammad Bilal Virk
AI automation engineer — building agents, workflows, and RPA that remove repetitive work.
Share
Newsletter

One email, when I ship something worth reading.

No cadence, no filler. Unsubscribe any time.

Free consultation

Want this built against your real numbers?

A 30-minute call to scope the workflow, agent, or automation you actually need.

Book a free consultation

More developer tools

All tools
Next step

Have a workflow that's burning hours every week?

Bring me one real bottleneck. I'll tell you whether it's worth automating, and what it would take.

Book 30 Minutes Call