RE2 vs PCRE — The Differences That Break Your Regex (and the Exact Fixes)
Your pattern works in JavaScript, Python and your IDE — then fails silently in Google Analytics 4, Go, Rust or BigQuery. The reason is almost always the engine: those systems run RE2, which rejects entire families of PCRE syntax. This page maps every difference and gives you the replacement pattern for each.
TL;DR: RE2 supports character classes, groups, alternation, anchors, \b, Unicode classes and non-greedy quantifiers — but no lookahead, no lookbehind, no backreferences, no atomic groups, no possessive quantifiers, no recursion. Every one of those has a recipe below.
Which engines use RE2, which use PCRE
| Engine family | Members | Assertions? |
|---|---|---|
| RE2 (linear-time, no backtracking) | Google Analytics 4, Go regexp, Rust regex crate, BigQuery REGEXP_*, Google Sheets, Cassandra | No |
| PCRE / backtracking | Perl, PHP, Python re, JavaScript, Java, Ruby, .NET | Yes |
JavaScript deserves a note: the browser's RegExp is PCRE-like — it has lookahead/lookbehind and backreferences, but it is not identical to PCRE either (no \K, no conditionals). It behaves like PCRE for everything this page covers.
What RE2 does not support — and what to use instead
| PCRE construct | RE2 replacement | |
|---|---|---|
Lookahead foo(?=bar) |
Match the full text: foobar, or capture the part you need: (foo)bar → use group 1 |
Try in tester → |
Negative lookahead (?!admin) |
Negated character class for one position ([^x]), or split into an include filter plus an exclude filter in your tool (e.g. GA4) |
Try in tester → |
Lookbehind (?<=/)(\w+) |
Include the prefix in the match: /(\w+), then use the capture group |
Try in tester → |
Backreference (foo|bar)\1 |
Expand fixed alternatives — foofoo|barbar; dynamic equality checks belong in application code |
Try in tester → |
Atomic group (?>\d+) |
Plain group (\d+) — RE2 never backtracks exponentially, so atomicity is pointless there |
Try in tester → |
Possessive a++, \d*+ |
Normal quantifier a+, \d* — possessives exist only to stop backtracking, which RE2 does not do |
Try in tester → |
Conditional (?(1)a|b) |
Write both branches as an explicit alternation, or handle in code | |
Recursion (?R), subroutine ( ?1 ) |
Not reproducible in regex — parse with a real parser or iterate in code | |
\K (reset match) |
Adjust the pattern boundary, then use a capture group for the interesting part |
What RE2 fully supports (the good news)
Most day-to-day regex needs nothing from the unsupported list:
- Character classes
[a-z], negated[^/], ranges, Unicode classes\p{L},\p{N} - Shorthands
\w \d \sand their negations, word boundary\b - Groups & alternation — capturing, non-capturing
(?:…), named(?P<name>…),| - Quantifiers —
* + ? {n,m}and non-greedy*? +? - Anchors
^ $ \A \z \Z(though GA4's filters match the full string anyway) - Inline flags
(?i),(?m),(?s),(?U)
Why RE2 exists: catastrophic backtracking
PCRE engines backtrack: on adversarial input such as ^(a+)+$ matched against a long run of a that ends in X, the number of paths explodes exponentially and the CPU pins at 100% — the classic ReDoS (Regular Expression Denial of Service) attack. RE2's automaton-based matcher evaluates every position once, so that same pattern is harmless there. This is exactly why Google runs RE2 on untrusted input — GA4 filter definitions, BigQuery queries — and why the trade-off of losing lookahead was worth it for them.
(We deliberately do not link that pattern to the tester above — one click in a backtracking engine can freeze a browser tab. That freeze is the difference between the two engines.)
FAQ
Does RE2 support lookahead or lookbehind?
No. Assertions need backtracking or unbounded memory; RE2's linear-time guarantee rules them out. Use character classes, alternation, capture groups, or a second filter.
Is Go's regexp RE2?
Yes — Go's regexp package implements RE2 syntax (no lookahead, no backreferences). Same for Rust's regex crate and BigQuery's REGEXP_CONTAINS / REGEXP_EXTRACT.
Why does RE2 not support backreferences?
Backreferences make matching NP-complete in the general case. RE2 trades them for a hard O(input × pattern) time bound. Expand fixed alternatives ((foo|bar)\1 → foofoo|barbar) or compare strings in code.
Is RE2 always faster than PCRE?
No — for simple patterns on friendly input, backtracking engines can be faster. RE2 wins on worst-case behavior: no ReDoS, no pathological slowdowns, predictable latency on large inputs.
GA4 is your RE2 target? See the GA4 regex cheat sheet, the page path regex guide and the event filter regex guide.