Regular Expressions for Analytics: Matching and Testing URLs
Learn to write and test regex for analytics URLs with positive and negative cases, and see how GTM, GA4 and Data Studio differ on partial and full matching.
6 min readBranislav Mateas
Copy post as Markdown
On this page
A regular expression in an analytics tool decides which URLs count. One pattern can fire a tag, filter a report or group pages. The same pattern can give different results in different tools. The reason is usually not the syntax. It is whether the tool needs the pattern to match the whole value or only a part of it.
A reliable method has three steps. Write the cases that must match and must not match. Write the pattern. Then check how the target tool applies it.
Write the test cases before the pattern
Take a hypothetical online store. The goal is to count product detail pages. Their paths look like /products/blue-mug. The slug uses lowercase letters, digits and hyphens. The listing page, review subpages and the English copy of the store under /en/ must not count.
The near misses matter most. A pattern that matches every "Yes" row is easy to write. Most bad patterns fail on the "No" rows.
This pattern meets the rule:
^/products/[a-z0-9-]+/?$
^ ties the match to the start of the value. [a-z0-9-]+ needs at least one lowercase letter, digit or hyphen. A hyphen at the end of a character class is a literal hyphen. /? allows one optional trailing slash. $ ties the match to the end. Checked row by row: the listing page fails because nothing follows the last slash. The reviews page fails because $ does not allow /reviews. The /en/ path fails at ^. /products/Blue-Mug fails because B is not in the class.
GTM matches part of the value, so anchors do the work
In Google Tag Manager, "matches RegEx" is one of the operators for trigger conditions (Tag Manager help). The help example uses a short pattern for "pro" that matches pages such as products, promotions and professionals. The pattern only has to appear somewhere in the value.
That is why anchors carry the rule in GTM. Here is the same test list against the pattern without anchors:
Path
/products/[a-z0-9-]+
^/products/[a-z0-9-]+/?$
/products/blue-mug
Match
Match
/products/blue-mug/
Match
Match
/products/
No match
No match
/products/blue-mug/reviews
Match (wrong)
No match
/en/products/blue-mug
Match (wrong)
No match
/products/Blue-Mug
No match
No match
The unanchored version fails two of four negative cases. The trigger would still fire on every real product page, so a quick check of positive cases would not reveal the problem. Run the full list in GTM Preview mode before you publish.
Matching is case sensitive by default. The Tag Manager API reference says a regex condition becomes case insensitive only when it has an ignore_case parameter set to true. In the interface, this is the separate case-insensitive version of the regex operator.
The Tag Manager help pages above do not name a regex engine. Patterns without lookahead, lookbehind or backreferences are safe to reuse in GA4 and Data Studio. Data Studio documents RE2 syntax, and the GA4 regex help links to it. RE2 does not support these features. This is a practical inference, not a documented GTM rule.
GA4 matches the whole value by default
The GA4 regex help says Google Analytics uses full regex by default and is case sensitive. For a partial match, you add metacharacters yourself. The documented example is .*San Francisco.*, which returns both "San Francisco" and "South San Francisco". The same page lists where GA4 accepts regex: data filters, key events, segments, audiences, content groups and custom channel groups.
Report filters make both modes explicit. They offer "matches regex", "matches partial regex" and the two negative versions. Regex there is case sensitive and limited to 250 characters.
This changes how GTM habits behave:
blue-mug with "matches regex" returns nothing, because no path is exactly blue-mug.
/products/[a-z0-9-]+ with "matches regex" excludes the reviews page and the /en/ copy, because the full value must match. It also excludes /products/blue-mug/, because the pattern has no optional slash.
The anchored pattern from above gives the same result under both modes. Anchors do no harm in a full match.
Check the dimension's values before you write the pattern. If a value includes a query string, such as /products/blue-mug?color=red, a full match pattern must allow it. Adding (\?.*)? before the final $ does that.
Data Studio has one function for each mode
Data Studio documents REGEXP_MATCH as returning true when the value exactly matches the pattern. Its example: 'A' returns false for ABC123, while 'A.*' returns true. REGEXP_CONTAINS matches part of the value. The documentation calls REGEXP_CONTAINS(field, "pattern") functionally equivalent to REGEXP_MATCH(field, ".*pattern.*"). Both pages state that Data Studio uses RE2 syntax.
A calculated field turns the test list into a visible check. Create the field below, replacing the field name with your page path field. Add it as a dimension to a table next to the path, then sort by it. Every true row must be a product page, and every false row must not be.
Backslashes need care. The REGEXP_MATCH page warns that patterns with escape characters such as \ may need extra escaping in Data Studio. It mentions raw string literals as a way around this. A hostname pattern such as ^(www\.)?example\.com$ is the typical case. Test it on known values before you trust it in a chart.
A short test routine for any URL pattern
Before a pattern goes into a trigger, filter or calculated field, run the same list in a browser console. The code below was not run for this article. It uses JavaScript's own regex engine, so the slashes in the literal are escaped.
Two empty arrays mean the pattern meets the rule. A console pass is not a pass in every tool. JavaScript supports lookbehind and backreferences, but RE2 does not. Then check the target tool's match mode:
Tool and operator
Matches
Documented note
GTM "matches RegEx"
Part of the value
Case sensitive unless ignore case is set
GA4 "matches regex"
Full value
Case sensitive, 250 characters in report filters
GA4 "matches partial regex"
Part of the value
Case sensitive, 250 characters in report filters
Data Studio REGEXP_MATCH
Full value
RE2, extra escaping for backslashes
Data Studio REGEXP_CONTAINS
Part of the value
RE2
Watch alternation in every tool. ^/blog|/news$ does not mean "blog or news sections". It means "starts with /blog" or "ends with /news". So it matches /blog-archive/2019 and /old/news. The section rule is ^/(blog|news)/, which matches /blog/post and /news/item but not /blogger/x or /en/blog/post. Under a full match, extend it to ^/(blog|news)/.*.
To practise reading patterns, regex crossword puzzles are a useful exercise. Each cell must satisfy a row pattern and a column pattern, which trains the same habit as negative test cases.