Writing · Marketing Ops · Dec 11, 2025 · 6 min

Fixing a cross-domain tracking problem that shouldn't have taken two months

How to find a tracking fault: read the handshake in order, confirm each hop carries the parameter, and stop treating a cookie banner as the suspect. Two months of theories, then an hour once the chain was read properly.

2 monthschasing cookies
~1 hourthe actual fix

The ping came from our Southeast Asia team, about two months into their regional launch: paid attribution was dead, and had been the whole time. I opened the dashboard and it was worse than the message. Spend was flowing, credit wasn't. Organic had collapsed into direct. Every UTM we sent evaporated the moment a user crossed from the marketing site to the product domain. And here's the uncomfortable part about quantifying the damage: I can't tell you what share of traffic was misreporting, because the dashboard that would answer that question was the thing that was broken. Two months of paid budget, effectively unattributed.

The fix, once I read the header script instead of the consent banner, took about an hour. The bug is one of the most common failures in multi-domain setups, but the way it got misdiagnosed is a pattern I keep seeing in marketing ops, so here's the anatomy.

The symptom: two months chasing cookies

The setup: the SEA team copied the North America Webflow site for the regional launch. Sensible move, about 80% of the site was reusable as-is. The tracking infrastructure lived in the other 20%, and within days three things broke at once:

  • Paid campaign attribution failed across every campaign in the region.
  • Organic traffic reported as direct, the channel split collapsed.
  • UTM parameters vanished the moment users crossed from the Webflow marketing site to the separate product domain.

The team's diagnosis was cookies, and I'd have started there too: a new region means new consent rules, and blocked cookies produce exactly these symptoms, attribution vanishing, direct inflating. It's the right first guess. It just happened to be wrong, and it stayed the working theory for two months of consent-banner and storage-logic troubleshooting that never moved a number. The data died earlier in the tracking chain, and the cookie layer is just where the body surfaced. Meanwhile the marketing dashboard was effectively blind: no campaign performance, no channel-to-customer attribution, no defensible story for any of the paid spend running against the region.

header scriptcopiedlinkerwrong paircookie layerblamedthe symptom got two months, the cause got one hour
The bug lived in the first two links of the chain (a header script and linker still pointing at North America) while two months of debugging went to the cookie layer, where only the symptom lived.

The actual root cause

When I finally got into the setup, the issue surfaced in minutes: the tracking code had been copied verbatim from North America, domain references and all. The header script still pointed at the North America subdomain, so the linker was faithfully bridging two domains that belonged to another region.

The architecture matters here. The Webflow site runs on AWS; the product portal lives on a separate domain and server. For attribution to survive that crossing, three things have to agree on which domains are actually in play:

  • The linker. The mechanism that decorates outbound links with a session identifier so it physically crosses the domain boundary.
  • The cross-domain configuration in the Google tag. The list of domains the tag treats as one property.
  • The custom header code, which must reference the real regional subdomain for the launch it is running on.

All three were still configured for a different region. No amount of cookie consent work was ever going to fix that.

Three-part handshake · SEA site → portal
“Configure your domains” list→ NA
Linker · outbound links carry _gl→ NA
Receiving tag · accepts _gl on arrival→ NA
After the one-hour fix
SEA pair in the domain list
_gl survives the crossing
The three-part handshake: the Google tag's domain list, the tag on the sending side decorating outbound links with _gl, and the tag on the receiving side accepting it. In GA4 the decoration and the acceptance both come free once both domains sit in that list, which is exactly why a wrong list breaks all three at once. All three were still pointing at North America.

The one-hour fix

Once I'd named the root cause, the repair was almost anticlimactic. I made four changes in Google Tag Manager:

  • Opened the Google tag's cross-domain measurement settings (the “Configure your domains” list) and replaced the North America entries with the pair that actually mattered here: the SEA marketing domain and the product domain.
  • Verified the linker was firing: links out to the product domain now carried the _gl parameter, which is how the session identifier survives the crossing.
  • Rewrote the header script with the correct SEA subdomain references and stripped the North America-only logic that had come along in the copy-paste.
  • Confirmed the receiving end accepted decorated links once the pair was listed. So the _gl parameter was read on arrival instead of discarded.

I can't show you a before-and-after percentage chart for the two broken months, that's precisely what a blind dashboard costs you. What I can show you is the check I ran: click a tagged link, cross the domain, watch _gl appear in the URL and the UTMs land in the backend. Before the fix, that check failed every single time. After, it passed every single time. Binary bugs are like that. Paid campaigns started attributing the same day, organic and direct separated back into distinct channels, and funnel data reconnected to customer lifetime value.

Keeping attribution alive across the handoff

Part of the rebuild was a small piece of custom code I wrote to guard the marketing-site-to-product handoff, the exact seam where the data had been dying. In outline, it:

  • Captures an inviteCode parameter from the landing URL.
  • Appends that code to every sign-up button and cross-domain redirect, so the identifier physically travels with the user.
  • Falls back to a default organic_google attribution when no inviteCode is present. An explicit default beats a silent blank.
// the seam that matters: Webflow → product portal const code = new URLSearchParams(location.search).get('inviteCode'); const attribution = code || 'organic_google'; // explicit default, never blank signupLinks.forEach(a => { const url = new URL(a.href); url.searchParams.set('inviteCode', attribution); a.href = url.toString(); });
Design principle: attribution should always degrade to a labeled default. “organic_google” can be argued with; an empty field can't even be found.

The honest trade-off in that default: it also catches paid visitors whose UTMs get stripped by some future bug, and labels them organic_google. Which is wrong. I accepted that because the two failure modes aren't symmetric. A blank field disappears into direct and nobody asks about it for two months; a suspicious spike in organic_google is visible, queryable, and gets investigated within a day. The default gives the next bug a name someone will notice.

Two ways to lose a UTMthe seam: marketing site to product portalBlank fieldthe silent failureWhere it landsDirectHow it readsOrdinaryQueryableNoTime to noticeTwo monthsorganic_googlethe labeled defaultWhere it landsOrganic, labeledHow it readsA spikeQueryableYesTime to noticeOne dayThe default also catches paid visitors whose UTMs get stripped, and that was the accepted price of giving the next bug a name.
Both outcomes record the wrong source. Only the labeled one leaves a trace someone can query, which is the difference between a day and two months.

What I documented afterward

The fix took an hour. Making sure the next regional launch doesn't repeat the two months took a bit longer, and was worth more. The post-fix documentation I wrote covers:

  • Linker setup and linker rules, step by step.
  • Domain mappings and GTM configuration per region.
  • Header code patterns and naming conventions.
  • A cross-domain checklist to run before any regional site goes live.
  • Validation steps: how to prove attribution survives the handoff before spending against it.

That documentation became the baseline for consistency across the international sites I ran tracking for, and, less measurably but just as real, it changed how regions collaborate: the checklist is now the conversation, instead of a two-month mystery.

If your UTMs disappear between domains

What I'd tell a peer walking into the same symptoms: check these before you touch the consent banner:

  • The “Configure your domains” list in your Google tag settings, does it name the domains you actually run, in this region?
  • The _gl parameter, does it appear on links crossing to the second domain? If not, the linker isn't firing.
  • Copied header scripts, do they reference the real subdomain, or the one they were copied from?
  • The handoff itself, does anything explicitly carry attribution across, or does the setup silently assume the linker alone will?

In our case, all four were wrong at once. Each one is checkable in under an hour.

Suggested posts