Why does the _gl cross-domain linker parameter go missing or expire between my two domains?
Quick answer
The _gl linker parameter is generated dynamically by gtag.js at the moment a link is rendered or clicked, and it's only valid for a short window (a couple of minutes) after being generated - so it goes missing when something outside gtag.js's control serves the link (server-side rendering that caches the HTML, a redirect service, an email client stripping query parameters), and it expires (producing a 'bad or expired linker parameter hash' condition) when there's a delay - a slow redirect chain, a bookmarked link clicked much later, or a cached page - between generation and the destination reading it.
Why This Happens
gtag.js decorates outbound links to configured cross-domain destinations in the browser, live, right before or during the click - it rewrites the link's href to append _gl=... at that moment. Anything that serves a pre-rendered or cached version of that link (a CDN cache, a static site export, an email template, a URL shortener that stores the destination URL once) captures the link before decoration happens, or captures a decoration that's already stale by the time someone clicks it.
The 'expired' half of this is a deliberate security/integrity feature, not a bug: the linker parameter is time-bound and hash-verified specifically so it can't be harvested and replayed indefinitely to impersonate a session. A linker parameter that sits in a bookmarked URL, a shared screenshot, or a slow multi-hop redirect for more than a couple of minutes is expected to fail its freshness check on arrival.
Fix It
Never hardcode or cache a decorated link
If a link to a cross-domain destination is being generated once and reused (in a static export, an email template, a CMS field), it will not carry a fresh _gl value at click time - the destination URL should be plain, and let gtag.js decorate it live in the visitor's browser on that page load.
Check for redirect chains eating the parameter
If the click passes through a URL shortener, an internal redirect, or a marketing-automation click-tracking link before reaching the destination domain, confirm each hop explicitly forwards all query parameters rather than dropping them - many redirect services strip unrecognized parameters by default.
Confirm both domains are in the same cross-domain configuration
See the dedicated source/medium cross-domain page - if the destination domain was never added to the cross-domain measurement list, there's no decoration to go missing in the first place, which can look identical to an expiration issue from the outside.
For server-rendered apps, decorate links server-side using the same logic gtag.js would apply
If your architecture genuinely requires generating the link before it reaches the browser (e.g. a server-rendered email or a non-JS context), you'll need to replicate GA4's linker parameter generation server-side using the client_id/session_id you already have - this is an advanced pattern and should be validated carefully against real cross-domain sessions before relying on it.
How To Verify It Worked
Click a live (never cached, never bookmarked) link from the first domain to the second in an incognito window immediately after the page loads, and confirm the destination URL carries a fresh _gl parameter that GA4 accepts - check GA4 DebugView on the destination domain for the session continuing under the same client thread rather than starting fresh.
To isolate an expiration issue specifically, deliberately wait several minutes between copying a decorated link and pasting it into a fresh browser tab - if the session fails to stitch only in this delayed scenario but works fine on an immediate click, that confirms expiration (normal, expected behavior) rather than a configuration problem to keep chasing.
Still Not Fixed?
We audit and fix GA4 implementations for a living - if this doesn't resolve it, the next step is usually a full event-by-event audit.
Talk to a GA4 Consultant