TrackRev
Blog
11 min read
Attribution

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 of TrackRev.io

Muzahid Maruf, Founder

LinkedIn · X

On this page
  1. 01Why This Matters for Your Revenue
  2. 02How Browsers Actually Treat Subdomains
  3. 03The Three Places Attribution Actually Leaks
  4. 04A Working Cross-Subdomain Setup, End to End
  5. 05Why the Popular Tools Fail at This
  6. 06How TrackRev Handles This
  7. 07When NOT to Use TrackRev for This

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.

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.

ScenarioMarketing hostApp hostShared cookie possible?What breaks
Classic SaaS splitwww.yourdomain.comapp.yourdomain.comYes — set Domain=.yourdomain.comNothing, once configured correctly
Host-only defaultwww.yourdomain.comapp.yourdomain.comNo — cookie is scoped to wwwNew identity issued at app; source lost
localStorage identitywww.yourdomain.comapp.yourdomain.comNo — localStorage never crosses originsIdentity always resets in the app
Platform subdomainyourdomain.comyourapp.vercel.appNo — different registrable domainsTrue cross-domain; needs link decoration
Apex vs wwwyourdomain.comapp.yourdomain.comYes — apex cookie covers subdomainsRedirect 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.

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.

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.

First-touch capture on www.yourdomain.com
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.

Attach attribution to Stripe Checkout on app.yourdomain.com
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 appliedPaid conversions credited correctlyReported 'Direct' shareRecovered monthly revenue attribution
Nothing (host-only cookie, bare redirect)37%58%$0 baseline
Cookie Domain set to .yourdomain.com58%39%+$4,200
+ UTM forwarded through redirect84%17%+$9,800
+ Stripe metadata identity stitching96%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.

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 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.

PostLinkedIn

Frequently asked questions

Muzahid Maruf — Founder of TrackRev.io

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.

Stop guessing where your Stripe revenue comes from.

Set up TrackRev in 5 minutes. Free tier covers 1,000 events / month — no card needed.