Regex Tester
Test regular expressions against sample text with live match highlighting. Supports the common flags (g, i, m, s, u, y), reports invalid patterns with clear messages and lists every match with its capture groups.
Enter a pattern and your test string; matches highlight live and are listed below with capture groups shown in distinct colors. Watch mode pauses any CPU-heavy matching until you hit the tick button.
Regular expression
Test text
/https?://[\w.-]+(?:/[^\s]*)?/g — 182 chars
Matches highlight live
Matches
2- #143–88
https://devworkbench.net/docs/getting-started - #297–131
https://github.com/search?q=nextjs
full match · g1 · g2
Runs locally on your browser's engine. Group offsets come from the native d flag, so duplicates stay exact. Pathological patterns are stopped after 400ms.
Learn more
The building blocks: anchors, classes and quantifiers
Every regular expression is built from three kinds of tokens. Anchors like ^ and $ don't match a character at all — they match a position, the start or end of the string (or of a line, with the m flag). Character classes like \d, \w and [a-f0-9] match exactly one character from a set. Quantifiers like *, + and {2,4} say how many times the token immediately before them may repeat. Almost every pattern you'll ever write is some combination of those three ideas stacked together.
The one genuinely confusing part is that a quantifier is greedy by default: .* tries to consume as much of the string as possible, then backtracks one character at a time until the rest of the pattern can match. That's why a pattern like <.*> against <b>bold</b> matches the entire string instead of just <b> — greedy .* first grabs everything, then gives back only as much as it must. Adding a ? after the quantifier (*?, +?) makes it lazy instead: it matches as little as possible, then expands only when forced to.
Capturing, not capturing, and naming groups
Parentheses (abc) do two things at once: they group a sub-pattern so a quantifier can apply to the whole thing, and they capture whatever that sub-pattern matched so you can retrieve it afterward. If you only need the grouping and don't care about capturing the text, (?:abc) is a non-capturing group — same grouping behavior, but it doesn't add an entry to the match's capture list, which keeps group numbering predictable in a longer pattern.
Named capture groups, (?<year>\d{4}), solve the readability problem of referring to "group 3" in code far from where the pattern was written — you access match.groups.year instead of match[3]. Backreferences like \1 let a pattern refer back to what an earlier group actually captured, which is how you match repeated words or balanced quotes: (['"]).*?\1 matches a quoted string using whichever quote character opened it.
Catastrophic backtracking, in plain terms
Backtracking regex engines (which is what JavaScript, Python, PCRE and most others use) try one path through the pattern, and if it fails, back up and try another. That's usually invisible and fast. It stops being fast when a pattern has nested or adjacent quantifiers that can match the same characters in many different ways — something like (a+)+b against a string of 30 a's with no trailing b. The engine ends up trying an exponential number of ways to split those a's among the groups before giving up, and the browser tab appears to freeze.
This is why a regex tester that respects a time budget matters for more than convenience: a pattern that looks fine against a short test string can still be dangerous (a real "ReDoS", regex denial-of-service, vector) once it runs against attacker-controlled input of unbounded length. The practical fix is almost always the same — avoid nested quantifiers over the same character class, or use an atomic/possessive construct where your regex flavor supports one.
What regex is (and isn't) good for
Regular expressions are excellent at matching patterns in flat, regular text: a phone number's digit grouping, a log line's timestamp, a slug's allowed characters. They're a poor tool for anything genuinely recursive or context-sensitive — matching balanced parentheses to arbitrary depth, or fully parsing HTML or JSON, needs a real parser, not a bigger regex, because regular languages can't express unbounded nesting by definition.
Email validation is the classic cautionary example: the actual grammar for a valid email address (RFC 5322) is large enough that a fully compliant regex is hundreds of characters long and still debated. Nearly every "email regex" you'll find online is a practical approximation that rejects some valid addresses and accepts some invalid ones — which is fine for a client-side sanity check, but the only way to truly validate an email address is to send it a message.
What each flag actually changes
The i flag makes matching case-insensitive across the whole pattern — useful any time you're matching user input where casing shouldn't matter. The g (global) flag changes what the match methods return: without it, .match() stops at the first match; with it, you get every match in the string, which is what most "find and replace all" use cases need. The m (multiline) flag changes what ^ and $ mean — normally they anchor to the start and end of the whole string, but with m they anchor to the start and end of each line instead.
The s (dotAll) flag makes . match line breaks too, which it doesn't by default — handy when a pattern needs to span multiple lines, like matching an entire multi-line comment block. The u (unicode) flag switches the engine to treat the pattern and string as sequences of actual Unicode code points instead of raw UTF-16 code units, which matters for correctly matching characters outside the Basic Multilingual Plane, like most emoji. The y (sticky) flag is the least common: it anchors matching to exactly the engine's current position (lastIndex) rather than letting it search forward, which is mainly useful when hand-writing a tokenizer.
Frequently asked questions
How do I make my regex case-insensitive?
Check the i flag (or append the i modifier). Other common flags are g (global), m (multiline) and s (dotal — makes . match newlines).
Why is my pattern slow to process large text?
Some patterns cause catastrophic backtracking. The tester enforces a time budget and shows matches progressively so the page never freezes.
What do the capture group colors mean?
Each numbered group in your pattern gets its own highlight color in the match list, so you can see exactly which part of the match each () captured.