What Cross-Domain Tracking Actually Does (and Why It Breaks)
Last quarter I audited a B2B SaaS company that runs its marketing site on www.brand.com and its checkout on app.brand.io. Google Ads showed 340 conversions. GA4 showed 122. The gap was not a consent issue or a tag-firing problem. It was a broken cross-domain tracking setup that split every checkout visitor into a brand-new session.
Cross-domain tracking solves one specific problem: when a user navigates from Domain A to Domain B, GA4 needs to know it is the same person in the same session. Without it, the destination domain creates a fresh client ID, and your reports show a self-referral instead of the original source. Every conversion on Domain B gets misattributed.
GA4 handles this by appending a _gl parameter to outbound links. That parameter carries the visitor's client ID from the source domain to the destination domain, so the Google tag on the receiving side can continue the session instead of starting a new one.
Simple concept. Fragile execution.
When You Actually Need Cross-Domain Tracking
Not every multi-domain setup requires it. Here is the decision framework I use:
| Scenario | Need cross-domain? | Why |
|---|---|---|
blog.brand.com + brand.com (subdomains) | No | GA4 tracks subdomains of the same root domain automatically |
brand.com + shop.brand.io (different root domains, same business) | Yes | Different root domains mean different first-party cookie jars |
brand.com + partnersite.com (separate businesses) | Usually no | Separate GA4 properties are cleaner unless you truly share a funnel |
brand.com + embedded payments.stripe.com iframe | Edge case | See the iframe section below |
The rule is straightforward: if a user clicks a link from one root domain to a different root domain and both domains belong to your property, you need cross-domain tracking. If the domains do not link to each other within a session, skip it --- the setup adds complexity for zero analytical value.
GA4 Cross-Domain Tracking Setup via the Admin UI
Google moved cross-domain configuration into the admin interface, and for most setups this is the recommended path. No code changes required.
- Open Admin > Data Streams and select your web data stream.
- Click Configure tag settings.
- Select Configure your domains.
- Add every root domain that should share a session (e.g.
brand.com,shop.brand.io). - Save.
GA4 will now automatically decorate outbound links to the listed domains with the _gl parameter. It also auto-manages the referral exclusion, so you no longer need to manually add domains to the "List unwanted referrals" setting the way you did in Universal Analytics.
Both domains must run the same GA4 measurement ID (same property, same data stream). If Domain B does not have the tag installed, or uses a different measurement ID, the _gl parameter will be ignored and the session will still break.
Cross-Domain Tracking with Google Tag Manager
If you manage tags through GTM --- and most of the setups I audit do --- the configuration is slightly different. Cross-domain tracking with Google Tag Manager involves setting the domain list on your Google Tag (formerly the GA4 Configuration tag):
- In GTM, open your Google Tag (the tag with your
G-XXXXXXXmeasurement ID). - Under Configuration settings, add a field named
linkerwith the value{"domains":["brand.com","shop.brand.io"]}. - If any cross-domain navigation happens through form submissions (e.g. a search form whose action points to a different domain), also add
"decorate_forms": trueinside the linker object. - Publish and verify.
Alternatively, you can configure the domain list in the GA4 admin UI as described above and let GTM fire the tag without additional linker settings. Google's own documentation recommends the admin-UI approach for most users, but the GTM method gives you more granular control --- especially when you need decorate_forms or when you are managing multiple properties in a single container.
The critical point with cross-domain tracking GTM setups: the domain list in GTM and the domain list in GA4 Admin must match. Mismatches are one of the most common reasons I see cross-domain tracking fail during tracking audits.
Verifying That It Works
Do not trust the configuration alone. Test it.
- Check the URL. Click a link from Domain A to Domain B. The destination URL should contain a
_gl=parameter. If it does not, the decorator is not firing. - Check the client ID. Open the browser console on both domains and run
document.cookie. The_gacookie value should contain the same client ID on both sides after navigation. - Use DebugView. In GA4, enable DebugView and walk through the cross-domain journey. Both page_view events should appear under a single device, not two.
- Check the Realtime report. Navigate from Domain A to Domain B and confirm you see a single active session, not two.
- Inspect traffic sources. After a few days of data, open the Traffic Acquisition report and filter by source. If you see
shop.brand.ioappearing as a referral source, the setup is broken.
The _gl Parameter: What Can Go Wrong
The _gl parameter is the linchpin of GA4 cross-domain tracking. When it fails, everything fails. Here are the failure modes I encounter most often:
Redirects strip the parameter. If Domain B's landing page performs a 301 or 302 redirect (e.g. enforcing a trailing slash or redirecting HTTP to HTTPS), the _gl parameter is often dropped during the redirect. Fix: configure your server to preserve query parameters through redirects.
Security rules block it. Web Application Firewalls (WAFs) and server-side security rules sometimes reject URLs containing unexpected query parameters. The _gl parameter can trigger these filters. Fix: whitelist _gl in your WAF or CDN rules.
JavaScript-driven navigation. The built-in decorator only works on standard <a> tag link clicks. If your site uses window.location.href or a JavaScript router for cross-domain navigation, the _gl parameter will not be appended automatically. Fix: use the glBridge utility or manually decorate the URL before navigation.
Form submissions. By default, GA4 does not decorate form action URLs. If a form on Domain A submits to Domain B, you must enable decorate_forms in your linker configuration.
If these edge cases sound familiar and you are not sure whether your setup handles them, I can audit your tracking and tell you exactly what to fix.
Iframe Cross-Domain Tracking
Iframe cross-domain tracking is a special case that GA4's built-in cross-domain measurement does not cover. When you embed an iframe from a different domain (e.g. a third-party booking widget or payment form), the iframe runs in a separate browsing context with its own cookie jar. The _gl decorator cannot reach it because there is no link click between parent and iframe.
The most reliable approach is the postMessage method:
- Install a lightweight script inside the iframe that sends interaction events to the parent page via
window.postMessage. - On the parent page, add a GTM Custom HTML tag that listens for these messages and pushes them into the dataLayer.
- Use standard GA4 event tags in the parent container to fire events from the dataLayer.
This approach keeps all tracking unified under the parent page's GA4 session. The iframe itself does not need its own GA4 tag.
Security note: Always validate event.origin in your postMessage listener. Only accept messages from the specific domain you expect.
For complex iframe tracking scenarios --- especially those involving consent management and server-side tracking --- the implementation gets nuanced quickly. A misconfigured setup can either miss events entirely or double-count them.
Browser Restrictions That Affect Cross-Domain Tracking
Even a perfectly configured setup faces headwinds from browser privacy features.
Safari ITP caps the lifetime of JavaScript-set cookies to 7 days of Safari use without interaction. If a user visits Domain A, receives a client ID cookie, and does not return for 8 days, the cookie is gone. Worse: if the user arrived via a decorated link (containing tracking parameters like gclid), ITP caps the cookie to just 24 hours.
This matters for cross-domain tracking because the _gl parameter carries a client ID that was set via JavaScript. On Safari, that client ID may already be expired by the time the user returns, breaking session stitching retroactively.
Firefox Enhanced Tracking Protection partitions cookies by top-level domain, which can interfere with scenarios where third-party contexts are involved.
The mitigation strategy is to set the GA4 cookie server-side via the Set-Cookie HTTP header, which is exempt from ITP's 7-day cap. This is one of the practical advantages of server-side tracking and a reason I recommend it alongside any cross-domain implementation for sites with meaningful Safari traffic.
Cross-Domain Tracking and Consent Mode
If you have implemented Google Consent Mode v2, be aware of how it interacts with cross-domain tracking.
When a user denies analytics cookies, GA4 fires in cookieless mode and does not write a client ID cookie. The _gl decorator may still append a parameter, but without a stored client ID on the destination domain, session continuity relies entirely on Google's modeled data.
In practice, this means your cross-domain tracking accuracy directly depends on your consent rates. If 40% of your users decline analytics consent, roughly 40% of your cross-domain journeys will not be tracked deterministically. This is another reason why first-party data strategy matters more than ever --- relying on client-side cookies alone leaves growing gaps in your data.
Debugging Checklist
When cross-domain tracking in Google Analytics breaks, I work through this checklist:
- Is the same measurement ID (
G-XXXXXXX) installed on both domains? - Are both domains listed in Admin > Data Streams > Configure your domains?
- If using GTM, does the linker domain list match the GA4 admin configuration?
- Does the
_glparameter appear in the destination URL after clicking a cross-domain link? - Is the destination page stripping query parameters via redirects or URL rewrites?
- Is a WAF or CDN blocking URLs containing the
_glparameter? - Are form submissions decorated (is
decorate_formsenabled)? - Is consent mode blocking cookie storage for a significant share of users?
- On Safari, are cookies being set server-side to avoid the ITP 7-day cap?
- For iframes, is the postMessage bridge implemented and validated against the correct origin?
If you tick all ten boxes and conversions still do not match between GA4 and Google Ads, the problem may be upstream --- attribution windows, conversion counting methods, or tag firing conditions.
FAQ
What is cross-domain tracking in GA4?
Cross-domain tracking is a GA4 feature that lets you track a single user session across multiple root domains. It works by appending a _gl parameter to URLs so the destination domain can continue the session instead of starting a new one. Without it, each domain creates a separate user and session, breaking attribution.
Do I need cross-domain tracking for subdomains?
No. GA4 automatically tracks subdomains of the same root domain as a single site, as long as they share the same measurement ID and data stream. Cross-domain tracking is only needed when users navigate between different root domains, such as brand.com and shop.brand.io.
Why is my cross-domain tracking not working?
The most common causes are mismatched domain lists between GTM and GA4 Admin, server-side redirects that strip the _gl parameter, WAF rules blocking unknown query parameters, and missing tag installation on the destination domain. Check that the _gl parameter survives the full redirect chain to the final landing page.
Can I track users inside an iframe from a different domain?
GA4 native cross-domain tracking does not work for iframes because there is no link click between the parent page and the iframe. The recommended approach is to use the JavaScript postMessage API to send events from the iframe to the parent page, then fire GA4 events from the parent container. This keeps tracking unified under a single session.
Does cross-domain tracking work when users block cookies?
When analytics cookies are blocked via consent denial, GA4 falls back to cookieless pings and modeled data. The _gl parameter may still be appended to URLs, but without a stored client ID on the destination domain, deterministic session stitching is not possible. Your cross-domain tracking accuracy will be proportional to your consent acceptance rate.
Not sure your cross-domain tracking is actually working? Book a tracking audit --- I will map every gap and tell you exactly what to fix.