Split URL Testing
Overview
Split URL tests route visitors to entirely different pages (URLs) to compare performance. Convert supports two ways to run them:
- Split URL Experience Type (recommended) — Handles everything automatically
- Manual redirects — Using
convert.redirect()andconvert.refresh()in other experience types
How Split URL Bucketing Works
Unlike standard A/B tests where variations modify the current page, split URL tests require navigation — the visitor must be sent to a different URL. This creates a challenge: how do you count the visitor if the redirect might fail?
Convert solves this with a deferred bucketing mechanism:
sequenceDiagram
participant V as Visitor
participant S as Script
participant C as Cookie
participant B as Backend
V->>S: Loads original page
S->>S: Decides variation
S->>C: Stores decision in _conv_sptest (15s TTL)
S->>V: Redirects to variation URL (or refreshes original)
V->>S: Loads target page
S->>C: Reads _conv_sptest
S->>B: Sends bucketing event
S->>C: Deletes _conv_sptest
The Three Phases
- Decision — The script decides which variation the visitor gets and stores the decision in the
_conv_sptestcookie (15-second TTL) - Navigation — The visitor is redirected (variation) or the page is refreshed (original)
- Tracking — After the page loads, the script reads the split cookie, sends the bucketing event, and deletes the cookie
Key insight: The visitor is only counted after navigation succeeds. If the redirect fails (network issue, JavaScript blocked, visitor closes tab), the visitor is never counted. This prevents Sample Ratio Mismatch (SRM).
Why We Refresh the Original
You might wonder: why does the original variation also require a page refresh? The answer is symmetry.
If only the variation redirects, then:
- Original visitors are counted immediately (no navigation barrier)
- Variation visitors are counted only after successful redirect
- This creates SRM because the original includes visitors who would have abandoned during redirect
By refreshing the original, both variations go through the same flow:
- Both break page execution
- Both require successful navigation to be counted
- Failed navigations are excluded equally from both groups
Manual Redirect API
When using A/B or Multivariate experience types with manual redirects, use these methods:
convert.redirect(url)
Navigates the visitor to the specified URL. Use in Variation JavaScript.
// Variation JS
convert.redirect('/new-landing-page');convert.refresh()
Reloads the current page. Use in Original Variation JavaScript to pair with the redirect.
// Original Variation JS
convert.refresh();Correct Pattern
// Original Variation JS
convert.refresh();
// Variation JS
convert.redirect('/new-landing-page');Common Mistakes
| Mistake | Why It Causes SRM |
|---|---|
Putting redirect() in ExperienceJS instead of VariationJS | Original visitors bypass the navigation barrier entirely |
Missing refresh() in Original VariationJS | Original is counted immediately; variation is counted after redirect |
Conditional redirect logic (if (url === X) redirect(Y)) | Some visitors redirect (counted after), others stay (counted immediately) |
The Split Cookie
The _conv_sptest cookie has a deliberately short lifetime:
- 15-second TTL — Prevents stale bucketing data from persisting
- Always written — Even when cookie consent hasn't been given (it's strictly functional)
- Deleted after use — Removed once the bucketing event fires
If navigation doesn't complete within 15 seconds, the cookie expires and the visitor is bucketed fresh on the next page load.
When to Use the Split URL Experience Type
The dedicated Split URL experience type is recommended because it:
- Handles refresh/redirect pairing automatically
- Prevents the common SRM mistakes listed above
- Supports "Transfer Original URL variables to the variation URL" for query parameter preservation
- Handles all edge cases without custom JavaScript
Use manual redirects only when you need complex conditional logic that the Split URL experience type can't express.
When a Browser or Extension Blocks the Redirect
A split URL test only counts a visitor once the navigation succeeds, so anything that stops the
navigation costs you that visitor. Before the redirect, the script cancels the page's own in-flight
requests so a heavy entry page cannot starve its own redirect. That cancellation cannot be undone —
so if something then blocks the navigation, the visitor is left on a page that stopped loading
part-way through.
In practice this is rarer than it sounds, and most of the conditions that sound alarming turn out to
be harmless. What matters is that losing a visitor from your counts and showing that visitor a
broken page are two different outcomes, and the common conditions cause only the first.
| Condition | What the visitor sees | Effect on your test |
|---|---|---|
| An ad blocker or privacy extension blocks the Convert script — by far the most common setup | The normal page, exactly as if no test were running | The visitor is never entered into the test at all, so your split stays balanced |
| An extension blocks the destination URL itself (for example uBlock Origin's strict blocking) | The extension's own block page, with an option to proceed | The visitor is not counted — a possible source of SRM |
| An extension or a script on your own site cancels programmatic navigation | The original page, stopped part-way through loading | The visitor is not counted — a possible source of SRM |
Your page is served with a Content-Security-Policy: sandbox policy that allows scripts but not top-level navigation | The original page, stopped part-way through loading | The visitor is not counted — a possible source of SRM |
Firefox's accessibility.blockautorefresh setting is enabled | The destination page — the redirect still works | None |
Two things bound the risk:
- Only a redirect that runs during page load can leave a partly-loaded page. A
convert.redirect()you trigger later — from a click handler or a timer, once the page has
finished loading — has nothing left to interrupt. - The visitor needs both halves. They have to be assigned to a redirect variation and have the
navigation blocked while the script itself was allowed to run. A browser or extension aggressive
enough to block the navigation usually blocks the Convert script first, which is the harmless
first row above.
Content Security Policy Is Not a Factor
If you run a strict CSP, it does not interfere with split URL redirects. No CSP directive restricts
where a page may navigate — the navigate-to directive was removed from the specification in
September 2022 and never shipped in any browser, and form-action applies to form submissions only.
Two CSP settings are worth knowing about, both unusual:
script-src(orsandboxwithoutallow-scripts) blocking the Convert script stops the test
running at all. Visitors see your normal page and are not entered into the test.sandboxwithallow-scriptsbut withoutallow-top-navigationis the one policy that
lets the script run and then refuses the redirect. If you serve a sandbox policy on the pages
under test, addallow-top-navigation.
Separately, connect-src and img-src can block the script's own reporting requests. That does not
affect the redirect, but it does hide the evidence — so rule it out before concluding a redirect
failed.
What to Do About It
There is nothing to fix in your test setup for the common cases, and no configuration on Convert's
side changes them — the navigation is refused by the visitor's browser. What helps:
- Serve the script from a first-party domain. This is the single biggest lever, because it moves
the most common condition (the script being blocked) out of play. - Check your own site's scripts. If anything on your pages intercepts or cancels navigation, it
will cancel a split URL redirect too. - Prefer the Split URL experience type over manual redirects, and keep
convert.refresh()in
the original, so both arms face the same navigation barrier and blocked navigations drop out of
both groups instead of one. - Read SRM in context. Some loss to blocked navigation is expected and affects both arms. Treat
a persistent, one-sided gap as an implementation problem first — the mistakes table above — not as
blocker interference.
Debugging
- SRM warning in the Convert app — Check if Original has significantly more visitors than expected
- Inspect
_conv_sptest— Should appear briefly (15 seconds) after page load - Network tab — Bucketing events (
/track/requests) should appear only after redirect/refresh, not before - Quick test — If you can trigger bucketing without a page reload, the implementation is incorrect
Updated 17 days ago