You build a regex, test it in your editor, it works. You paste it into a GA4 segment or comparison and... zero rows. No error, no warning, just silence.
Two features of GA4 explain almost every one of these failures.
1. GA4 matches the whole value, not a substring
In JavaScript, /blog/ matches "/blog/my-post" happily. In GA4, filters use full-match semantics: /blog/ matches the value /blog and nothing else.
That's why every GA4 pattern you find in the wild is wrapped in .*:
# matches any page path containing /blog/
.*/blog/.*
Two flavors to remember:
-
"is one of my regex patterns" (segment dimensions): full match, wrap with
.* - "partially matches regex" (some comparison surfaces): substring match, no wrapping needed
Test the exact flavor you'll use - mixing them up is the number one source of "my regex works in the preview but not in the report".
2. GA4 runs RE2, not JavaScript's RegExp
GA4 (like BigQuery, Go, and Rust) uses Google's RE2 engine. RE2 is linear-time - it can never blow up on adversarial input - but that guarantee costs it three PCRE families:
-
No lookahead / lookbehind:
foo(?=bar),(?<=/)(\w+)- rejected -
No backreferences:
(foo|bar)\1- rejected - No atomic groups / possessive quantifiers / recursion - rejected
The RE2-friendly mindset:
| Instead of | Write |
|---|---|
foo(?=bar) |
(foo)bar - capture the part you need |
negative lookahead (?!
|
a negated class [^x], or an exclude filter |
| `(foo | bar)\1` |
| non-greedy tricks | plain .* - GA4 matches the full value anyway |
Three patterns that actually work in GA4
Page path filter - all blog posts:
.*/blog/.*
Event name filter - the ecommerce funnel:
(view_item|add_to_cart|begin_checkout|add_payment_info|purchase)
Custom channel grouping - organic social:
.*(facebook|instagram|linkedin|twitter|x\.com|t\.co|youtube).*
(Notice the escaped dot in x\.com - unescaped, it would also match xXcom.)
The workflow that stops the guessing
- Write the pattern in a tester that lets you paste your real values (the actual
page_pathrows, the actual event names - not your memory of them). - Check which engine it targets. JavaScript-valid is not RE2-valid; if your pattern contains
(?=,(?!or\1, it will never run in GA4. - Only then paste it into GA4.
I kept failing at step 2 myself, so I built RegexLab - a free, browser-only tester that shows match/no-match per line and keeps the RE2 gotchas visible. There's also a pattern library with tested, copy-paste patterns (dates, URLs, UTM parameters, log levels), each with an RE2-compatibility note, and a dedicated RE2 vs PCRE page listing every unsupported construct with its replacement.
Nothing leaves your browser - the whole thing is client-side JavaScript RegExp under the hood.
TL;DR
- GA4 = full match. Wrap substring patterns in
.* - GA4 = RE2. No lookahead, no backreferences, no lookbehind
- Test against your real data, with the right engine in mind, before you ship the pattern
What's the regex problem that cost you the most debugging time? Mine was a lookahead that silently matched everything in preview and nothing in production.
Top comments (0)