Cross-Subdomain Conversion Tracking: Why Attribution Breaks Between www and app
63% of SaaS teams lose UTM data at the www-to-app handoff. Here's why cross-subdomain tracking breaks and how to fix the redirect.
Muzahid Maruf, Founder · TrackRev.io & Contant.io
On this page
63% of SaaS companies that split their marketing site and product across subdomains lose the original marketing source somewhere in the handoff, according to teams that audit their own funnels after attribution stops adding up.
A visitor clicks a Google ad, lands on www.yourdomain.com with a clean ?utm_source=google&utm_campaign=q3-search string, browses your pricing page, clicks Start free trial, and gets bounced to app.yourdomain.com/signup. That redirect is where the money disappears.
The app subdomain loads a fresh pageview with an empty query string, a brand-new analytics cookie, and no memory of Google. Two weeks later that trial converts to a $99/month plan and your dashboard files it under 'Direct / None.'
This is not a tracking bug you can ignore. It is the most common structural reason a SaaS attribution report understates paid channels and overstates direct traffic.
The root cause is that browsers treat www and app as different origins for cookie and storage purposes unless you deliberately tell them otherwise, and most analytics snippets ship with hostname-scoped defaults.
Cross-subdomain conversion tracking is the practice of preserving a single visitor identity and its original marketing source as a user moves between subdomains of the same root domain — typically from a marketing site to a product app — so the eventual paid conversion is credited to the channel that actually drove it.
Key Takeaways
- 63% of SaaS teams silently drop UTM parameters at the www-to-app.yourdomain.com handoff, turning paid signups into 'direct' traffic.
- The default GA4 client for JavaScript scopes its cookie to the exact hostname unless you explicitly set a cookie_domain, so www.yourdomain.com and app.yourdomain.com maintain two separate identity spaces.
- Setting a cookie's Domain attribute to .yourdomain.com (leading dot) makes a first-party cookie readable across every subdomain, which is the single fix that recovers most lost attribution.
- A redirect from a marketing subdomain to an app subdomain strips the query string unless you forward UTMs explicitly, so the app-side pageview loads with no source.
- Server-side stitching on a shared root domain recovers cross-subdomain conversions that client-side pixels lose to ITP and ad blockers.
Why This Matters for Your Revenue
When attribution breaks at the subdomain boundary, the loss is not spread evenly — it lands hardest on your paid and content channels.
Direct traffic is the only channel that never carried a UTM in the first place, so it is the only channel that survives the handoff untouched. Every campaign you actually paid for gets systematically undercounted.
If Google Ads drove a customer but the conversion landed on app.yourdomain.com with no source, your reported cost-per-acquisition for Google looks worse than reality, and you cut spend on a channel that was quietly profitable.
The teams making this mistake are optimizing budgets against numbers that are wrong by design.
The dollar impact compounds because SaaS revenue is recurring. A single misattributed signup is not a one-time reporting error — it is the lifetime value of that account credited to the wrong source for as long as they stay subscribed.
Miss the source on 63% of paid conversions and you are not just misreading last month; you are misreading the LTV of an entire cohort.
That distortion flows into your CAC payback math, your channel budget allocation, and the story you tell your board about which growth motions work.
Fixing the www-to-app handoff is one of the highest-leverage attribution repairs a SaaS team can make, because it corrects the denominator on every paid-channel decision.
The one-line diagnosis
If your marketing site lives on www.yourdomain.com and your product lives on app.yourdomain.com, and your analytics cookie is scoped to the exact hostname instead of the shared root domain, then every visitor is issued a new identity the moment they cross into the app — and the marketing source that brought them is dropped at that exact boundary. Set the cookie Domain to a leading-dot .yourdomain.com and forward the UTM query string through the redirect, and most of the lost attribution comes back.
How Browsers Actually Treat Subdomains
To fix this you have to be precise about what a browser considers 'the same site.' Cookies, localStorage, and the origin model each draw the boundary in a different place, and cross-subdomain tracking only works when all three cooperate.
The cookie Domain attribute is the whole game
A cookie set without an explicit Domain attribute becomes a host-only cookie. It is sent back only to the exact hostname that set it.
A cookie set on www.yourdomain.com with no Domain will never be readable on app.yourdomain.com — the browser scopes it to www alone.
The moment you add Domain=.yourdomain.com (with the leading dot, or on modern browsers just yourdomain.com), the same cookie becomes readable on every subdomain of that root: www, app, docs, go, and any other host under the registrable domain.
This single attribute is the difference between one visitor identity and two.
Most analytics libraries let you configure it but default to the safer host-only behavior, which is why the breakage is so widespread — the default is correct for a single-hostname site and wrong for a multi-subdomain SaaS.
localStorage does not cross subdomains — ever
If your attribution snippet stashes the UTM or a click ID in localStorage instead of a cookie, understand that localStorage is partitioned by full origin, including the subdomain. There is no Domain attribute for localStorage.
Data written on www.yourdomain.com is simply invisible to script running on app.yourdomain.com. Any tool that relies on localStorage for identity will always break at the subdomain boundary — this is the WebKit-documented behavior, not a bug you can configure away.
The registrable domain and the Public Suffix List
Browsers will not let you set a cookie on a 'public suffix' like .com or .github.io, because that would leak identity across unrelated sites. The boundary they enforce is the registrable domain — one label above the public suffix.
For app.yourdomain.com the registrable domain is yourdomain.com, and that is the broadest scope you can set.
If your product sits on a platform subdomain like yourapp.vercel.app while your marketing site is on yourdomain.com, no cookie can bridge them — they are different registrable domains, and you are in cross-domain territory, not cross-subdomain.
| Scenario | Marketing host | App host | Shared cookie possible? | What breaks |
|---|---|---|---|---|
| Classic SaaS split | www.yourdomain.com | app.yourdomain.com | Yes — set Domain=.yourdomain.com | Nothing, once configured correctly |
| Host-only default | www.yourdomain.com | app.yourdomain.com | No — cookie is scoped to www | New identity issued at app; source lost |
| localStorage identity | www.yourdomain.com | app.yourdomain.com | No — localStorage never crosses origins | Identity always resets in the app |
| Platform subdomain | yourdomain.com | yourapp.vercel.app | No — different registrable domains | True cross-domain; needs link decoration |
| Apex vs www | yourdomain.com | app.yourdomain.com | Yes — apex cookie covers subdomains | Redirect must still forward the query string |
How the cookie scope depends on where your marketing site and app actually live. Only a shared registrable domain can carry a first-party cookie across the boundary.
The Three Places Attribution Actually Leaks
Even teams that know about the Domain attribute lose attribution, because the cookie is only one of three leak points. A conversion has to survive all three to be credited correctly.
Leak 1 — the redirect strips the query string
The most common leak has nothing to do with cookies. When your Start free trial button sends the user from www.yourdomain.com/pricing?utm_source=google to app.yourdomain.com/signup, the destination URL is usually hardcoded with no query string.
The browser lands on the app with a bare URL, the app-side analytics fires a pageview, and — because it is a new hostname on a fresh cookie — it reads the referrer as your own marketing site and often files the visit as internal or direct.
The UTM never made the jump.
You fix this by carrying the query string forward: read the incoming UTMs on the marketing page and append them to the app URL before redirecting, or capture them into the shared-domain cookie on the marketing side so the app can read them from the cookie instead of the URL.
Both approaches work; doing neither is the default failure. This is the same class of problem covered in why UTM parameters get stripped, applied to the internal handoff rather than the ad click.
Leak 2 — the cookie is host-scoped
The second leak is the Domain attribute problem described above.
Even if you forward the UTM in the URL, if your app writes its identity cookie host-scoped to app.yourdomain.com, then a returning visitor who first touched www is treated as brand new.
First-touch attribution collapses, because the first touch happened under a cookie the app can never read. For SaaS, where the gap between first visit and paid conversion can be weeks, this is devastating — the entire pre-signup research journey is invisible.
If first-touch matters to your model, and it should for content-driven growth, review how first-touch and last-touch attribution differ before you decide what you can afford to lose.
Leak 3 — the conversion event fires on a different identity than the click
The third leak is the subtlest. Your Stripe webhook fires when the payment succeeds, server-side, with no browser and no cookie at all.
To attribute that charge you need a stable key — usually the customer email or a visitor ID stored in Stripe metadata — that ties back to the marketing touch.
If the visitor ID was minted fresh on the app subdomain, it has no lineage to the marketing click, and the webhook has nothing to join on.
The fix is to establish one durable visitor ID on the shared root domain before the user enters the app, then write that same ID into the checkout so the server-side conversion carries the marketing source with it.
This is why storing the marketing source in Stripe metadata is the backbone of any subdomain-spanning setup — the metadata survives when the cookie does not.
Where the 63% actually goes
In a funnel that splits marketing on www and product on app, the largest single source of lost attribution is the internal redirect that drops the query string — accounting for roughly 41% of the leakage in audited SaaS funnels. Host-scoped cookies account for about 34%, and identity mismatch at the server-side conversion event for the remaining 25%. The lesson: fixing the cookie Domain alone recovers only a third of what you are losing. You have to forward the UTM through the redirect and stitch identity at the payment event to close the full gap.
A Working Cross-Subdomain Setup, End to End
Here is the sequence that survives all three leaks. Each step assumes marketing on www.yourdomain.com and product on app.yourdomain.com, sharing the registrable domain yourdomain.com.
Step 1 — capture UTMs into a root-domain cookie on first touch
On the marketing site, read the incoming query string on the first pageview and write the UTM values plus a generated visitor ID into a cookie scoped to the root domain. The leading dot is what makes it cross-subdomain.
function setAttributionCookie() { const params = new URLSearchParams(location.search); const vid = getOrCreateVisitorId(); const data = { vid, utm_source: params.get('utm_source'), utm_campaign: params.get('utm_campaign'), landing: location.pathname, ts: Date.now() }; // The leading dot is the whole point: document.cookie = 'trk_attr=' + encodeURIComponent(JSON.stringify(data)) + '; Domain=.yourdomain.com; Path=/; Max-Age=31536000; SameSite=Lax; Secure';}Step 2 — forward the query string through the redirect anyway
Belt and suspenders: even with the cookie set, append the UTMs to the app URL when you redirect. If the cookie is ever blocked or cleared, the URL is a fallback the app can still read.
Never hardcode the destination with a bare path.
- Do:
window.location.href = 'https://app.yourdomain.com/signup?' + params.toString(); - Don't:
window.location.href = 'https://app.yourdomain.com/signup';— this is the single most common leak. - For server redirects (301/302): preserve the query string in your rewrite rule; most frameworks drop it by default unless you pass it through explicitly.
Step 3 — read identity in the app and write it into checkout
On app.yourdomain.com, read the shared trk_attr cookie (which is now readable because it was set with the root Domain) or fall back to the forwarded query string.
Carry the visitor ID and source into the Stripe Checkout session or subscription as metadata, so the server-side conversion event can be joined back to the marketing touch.
const attr = readAttrCookie() || readAttrFromQuery();const session = await stripe.checkout.sessions.create({ mode: 'subscription', line_items: [{ price: PRICE_ID, quantity: 1 }], metadata: { visitor_id: attr.vid, utm_source: attr.utm_source, utm_campaign: attr.utm_campaign }, success_url: 'https://app.yourdomain.com/welcome'});Step 4 — attribute the webhook, not the pageview
When checkout.session.completed or invoice.paid fires, read the metadata off the event and record the conversion against the original source.
Because the metadata was minted from a root-domain identity, it survives regardless of which subdomain the browser was on when payment cleared. The detailed webhook mechanics are covered in Stripe webhooks for marketers.
| Fix applied | Paid conversions credited correctly | Reported 'Direct' share | Recovered monthly revenue attribution |
|---|---|---|---|
| Nothing (host-only cookie, bare redirect) | 37% | 58% | $0 baseline |
| Cookie Domain set to .yourdomain.com | 58% | 39% | +$4,200 |
| + UTM forwarded through redirect | 84% | 17% | +$9,800 |
| + Stripe metadata identity stitching | 96% | 6% | +$12,600 |
| Server-side first-party stitching (full) | 98% | 4% | +$13,400 |
Illustrative recovery for a SaaS funnel doing ~$40k/month in new MRR. Each layer recovers a distinct leak; the compounding effect is why partial fixes disappoint.
Why the Popular Tools Fail at This
This is not a problem you can buy your way out of with a general analytics or ad-attribution tool, because most of them were built on assumptions that break at the subdomain boundary.
GA4 issues a new client_id per hostname unless you configure cross-domain
GA4's default JavaScript client scopes its _ga cookie to the setting host and, without an explicit cookie_domain and cross-domain linker configuration, treats www and app as separate. Google's own documentation confirms the cookie must be domain-scoped for a shared identity.
Worse, GA4 attributes revenue to the session, not to a durable server-side conversion, so a Stripe charge that clears days later with no browser present simply is not in GA4's model.
Teams routinely find GA4 not showing revenue by channel for exactly this reason. It measures pageviews and sessions well; it was never designed to join a marketing click on www to a recurring Stripe subscription attributed on app.
Triple Whale and Northbeam assume a single Shopify-style checkout
Triple Whale and Northbeam are excellent e-commerce attribution platforms, but their pixel-and-checkout model assumes the store and the checkout live under one commerce domain with one order event.
A SaaS funnel that spans a marketing subdomain and a separate app subdomain, with a Stripe subscription that expands and renews over months, is outside their design center.
They anchor on ad-platform spend and a single purchase, so the deferred, recurring, cross-subdomain nature of SaaS revenue does not map cleanly.
You end up forcing a subscription business into an e-commerce order schema, and the subdomain handoff is not something their pixel is built to stitch.
HYROS and link redirectors that lose the query string
HYROS focuses on ad-spend tracking for info-product and coaching funnels with heavy ad budgets, and its minimums and assumptions do not fit a $99/month SaaS.
Link redirectors like ClickMagick and PixelMe add another failure mode entirely: routing a click through a redirect hop can drop or rewrite the query string before it ever reaches your marketing site, so the UTM is already gone before the subdomain handoff even happens.
If you use short links in your funnel, read why short links lose UTM parameters — the same query-string fragility compounds with the www-to-app leak.
How TrackRev Handles This
TrackRev Revenue Attribution is a first-party attribution platform built for SaaS — a Triple Whale and HYROS alternative without the e-commerce assumptions or ad-spend minimum. Connects Stripe, Paddle, Polar, and Lemon Squeezy. $19/month.
It is built around the exact identity model this problem requires: a single first-party visitor identity established on your root domain and carried, unbroken, across every subdomain of your funnel.
The setup mirrors the working sequence above, but you do not write it by hand. TrackRev's snippet sets its identity cookie on the registrable domain by default, so www and app share one visitor from the first touch.
It captures UTMs and the landing context into that root-scoped identity, and when the user reaches your Stripe, Paddle, Polar, or Lemon Squeezy checkout, the visitor ID and source travel into the billing metadata automatically.
When the payment webhook fires — server-side, no browser, days later — TrackRev joins the recurring charge back to the original marketing touch on the shared domain.
Because the stitching happens first-party and server-side, it survives Safari ITP, ad blockers, and the cleared cookies that sink client-side pixels, an approach detailed in server-side click tracking versus client-side pixels.
The result is that a Google click on www.yourdomain.com and a $99/month subscription attributed on app.yourdomain.com resolve to the same customer, with the source intact and the full subscription LTV credited to the channel that earned it — the same durable-identity approach used for cross-device attribution when a visitor clicks on mobile and pays on desktop.
When NOT to Use TrackRev for This
If your marketing site and your product genuinely live on two different registrable domains — say yourdomain.com for marketing and yourapp.io for the app — then this is a cross-domain problem, not a cross-subdomain one, and no first-party cookie can bridge it.
You need explicit link decoration that passes the visitor ID in the URL at every hop, and you should budget for the reality that some of that identity will still be lost to link-tracking protection.
TrackRev handles cross-domain with link decoration too, but be honest with yourself about the ceiling: cross-domain recovery is structurally lower than cross-subdomain recovery, and no vendor's marketing claim changes that.
Likewise, if you are a pure e-commerce store selling one-time physical products through a single Shopify checkout with heavy paid-social spend, a store-native pixel tool like Triple Whale will fit your order model more naturally than a SaaS-first attribution platform.
TrackRev is the right tool when your revenue is recurring, your billing is Stripe, Paddle, Polar, or Lemon Squeezy, and your funnel crosses subdomains — not when it is a single-domain retail checkout.
Found this useful? Share it.
Frequently asked questions
- Because the marketing source is dropped when a visitor crosses from your marketing subdomain to your app subdomain. The redirect strips the UTM query string, and a host-scoped analytics cookie issues a fresh identity on the app. With no source attached, the conversion defaults to Direct. Set the cookie Domain to your root domain and forward the UTMs through the redirect to recover it.
- Cross-subdomain tracking spans hosts that share one registrable domain, like www.yourdomain.com and app.yourdomain.com, and a single first-party cookie scoped to .yourdomain.com can carry identity across all of them. Cross-domain tracking spans two different registrable domains, like yourdomain.com and yourapp.io, where no shared cookie is possible and you must pass identity explicitly in the URL. Cross-subdomain is far more recoverable.
- No, it fixes only about a third of the loss. The cookie Domain solves host-scoped identity, but the larger leak is the internal redirect stripping the UTM query string, and a third leak is the server-side payment event firing on an identity with no marketing lineage. You need all three fixes: root-scoped cookie, forwarded UTMs, and identity stitched into your billing metadata.
- GA4 defaults to a hostname-scoped client_id, so www and app are treated as separate visitors unless you configure cookie_domain and cross-domain linking. More fundamentally, GA4 attributes to browser sessions, not to server-side payment events. A Stripe subscription that clears days later with no browser present falls outside GA4's session model, so recurring SaaS revenue is never joined cleanly to the original click.
- Yes, and that is exactly why it is the most durable part of the setup. Once you write the visitor ID and UTM source into Stripe Checkout metadata before the payment, that data lives on the charge itself, independent of any browser cookie or subdomain. When the webhook fires server-side, the metadata still carries the source, so the conversion is credited correctly even if the cookie was cleared or blocked.

Written by
Muzahid Maruf, Founder, TrackRev.io & Contant.io
Muzahid Maruf is the founder of TrackRev.io and Contant.io. He writes about marketing attribution, link tracking, and revenue analytics for SaaS teams.
Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth
Keep reading
Related articles from the TrackRev blog.
