# URL Matching

Choose which pages each test runs on — exact, simple, contains, wildcard, regex.

Every test has a target URL and a match type that decides which pages the test runs on. The SDK evaluates the match on every pageview (including soft navigations in single-page apps), so picking the right mode matters for both targeting and reporting.

URL matching is the single most common source of "why isn't my test running?" questions, and the single most common source of inflated denominators (test counts visitors on pages you didn't mean to target). It's worth two minutes to pick the right mode upfront rather than debugging in production later.

The five modes below trade specificity for flexibility. **Simple** is the right default for most tests because it ignores the things that don't matter (protocol, www, trailing slash) and respects the things that do (pathname). Reach for the others when you have a specific reason.

## Match types

### Exact match (`exact`)

Strictest mode. The visitor URL must match the test URL exactly, case-insensitively, after stripping the query string and hash. Protocol, subdomain, and trailing slash all matter.

```text
Test URL: https://shop.example.com/pricing
Matches: https://shop.example.com/pricing?ref=email
Does not match: http://shop.example.com/pricing or https://example.com/pricing
```

### Simple match (`simple`)

Forgiving equality. Ignores protocol (http/https), the leading www., and a trailing slash, in addition to query and hash. Best when you want one canonical page regardless of how visitors arrive.

```text
Test URL: example.com/pricing
Matches: https://www.example.com/pricing/, http://example.com/pricing?utm=x
Does not match: example.com/pricing/v2
```

### Contains (`contains`)

Substring match (case-insensitive) against either the full URL or the base URL (without query/hash). Use for groups of pages that share a path fragment, like every page under /blog/.

```text
Test URL: /blog/
Matches: https://example.com/blog/post-1, https://example.com/category/blog/x
Does not match: https://example.com/news
```

### Wildcard (`wildcard`)

Pattern matching where * stands in for any sequence of characters. The match is anchored to the start and end of the base URL (query and hash are ignored). Special regex characters in the rest of the pattern are escaped, so you can paste real URLs safely.

```text
Test URL: https://example.com/products/*/details
Matches: https://example.com/products/123/details, https://example.com/products/anything/details
Does not match: https://example.com/products/123 (no /details)
```

### Regex (`regex`)

Full regular expression evaluated case-insensitively against the visitor URL (including query string and hash). Standard regex syntax — stick to portable constructs (anchors, character classes, quantifiers) and test the pattern before launching. A typo here can silently match too few or too many pages.

```text
Test URL: ^https://example\.com/users/\d+$
Matches: https://example.com/users/42
Does not match: https://example.com/users/profile
```

## Wildcard redirects with `relay_params`

Redirect tests using **Wildcard** matching get one extra behavior when `relay_params` is enabled. If the variant's redirect URL points at the same base path as the wildcard, the SDK preserves the visitor's current pathname and only overlays the variant's query string and hash on top.

Example: test URL `https://shop.example.com/*` with variant URL `https://shop.example.com/?preview_theme_id=123` sends a visitor on `/products/jacket` to `https://shop.example.com/products/jacket?preview_theme_id=123`. This is the pattern used for Shopify theme previews and similar site-wide query-string overlays. Exact redirects (`/pricing → /pricing-v2`) and wildcards with a different destination path always honor the configured destination instead.

> **URL matching tips:**
>
> - **Default to Simple match when you can.** Exact match requires a perfect string equality (protocol, host, path, query, hash) — small differences like a trailing slash or appended UTM parameters will fail. Simple ignores query and hash, which is usually what you want for marketing pages.
> - **Use Wildcard sparingly.** Wildcards are powerful but make it easy to target more pages than you intended. Reach for them when you genuinely want a path family (a /blog/* archive, a /products/* catalog) — not as a workaround for forgetting which exact URL you mean.
> - **Watch the wizard's URL preview.** Below the URL field, the wizard shows a friendly summary of what your pattern will match ("All pages on your website" / "Only this URL" / "Pages matching this pattern") plus a warning if you picked Wildcard without a `*` character. If the summary doesn't describe what you want, adjust the match type before launching.

## Frequently asked questions

### Which match type should I use?

Start with Simple for most cases — it ignores http/https, www, and trailing slashes, which handles ~90% of real-world targeting needs. Use Exact when you need protocol-precise matching. Use Contains for groups of pages sharing a path fragment (like every /blog/ page). Use Wildcard for parameterized URLs. Use Regex only when you genuinely need full pattern power and have validated your expression.

### Why isn't my regex matching?

Three common culprits: (1) you didn't escape literal dots, so example.com matches example-com too; (2) you forgot to anchor with ^ and $, so the pattern matches anywhere in the URL; (3) you assumed case sensitivity — Otter always evaluates regex case-insensitively. Test your pattern with the visitor URL pattern from your analytics before saving.

### Are query strings and hash fragments part of the match?

By default, no — Exact, Simple, Contains, and Wildcard all strip ?query and #hash before comparing, so visitors arriving with UTM parameters or anchor links still get included. Regex is the exception: it sees the full URL including query and hash, so you can match on UTM tags or fragment-routed SPAs if you need to.

### What is relay_params and how does it work?

relay_params is a per-test setting that controls what happens to the visitor's query string and hash when a redirect test fires. Enabled (the default for most redirect tests), Otter merges the current URL's parameters into the destination URL so context like UTM tags, session IDs, and tracking parameters survive. Disabled, the destination URL is used exactly as configured.

### When does wildcard matching preserve the visitor's path?

Only when both conditions are met: the test uses wildcard matching, and the variant redirect URL's base path matches the wildcard's base path. In that case, the SDK keeps the visitor's pathname and overlays the variant URL's query string and hash. This is the pattern for Shopify theme previews — every page redirects to itself with ?preview_theme_id=123 appended.

### Can I run two tests on the same URL?

Yes, with one caveat: if both tests are Split URL / Redirect tests, only one can redirect at a time. Otter will pick whichever was assigned first and skip the other. Visual and Personalization tests can stack freely — the SDK applies their DOM changes in order and the variants combine on the page.

### What if I want to exclude specific URLs from a test?

Use the Targeting step's URL filters instead of trying to express the exclusion in match-type syntax. Targeting supports an explicit URL deny list with the same matching modes, which is cleaner and easier to maintain than complex regexes.

---

Canonical page: https://www.otterab.com/docs/building-tests/url-matching
