Quick answer: GA4 cross-domain tracking works only when the _gl URL parameter survives every redirect and link click between your domains. It gets stripped in 6 common scenarios: server-side redirects, iOS 17 Link Tracking Protection, WAF parameter filters, JavaScript-based redirects, mismatched cross-domain config, and CDN URL rewrites. Below is the audit method to detect each.
How GA4 cross-domain tracking actually works
When a user clicks a link from storedomain.com to checkoutdomain.com (both in your cross-domain config), the gtag library appends ?_gl=1*xyz... to the URL. The destination page reads this parameter on load and uses it to continue the same GA4 client_id/session_id rather than starting a new session.
If _gl doesn't survive the navigation, the destination page sees a new visitor, starts a fresh session, and breaks attribution.
The 6 ways _gl disappears
1. Server-side 302 redirects that drop query strings
The most common culprit. Your link points to checkoutdomain.com/cart?_gl=..., but the destination server has a redirect rule (e.g., enforce HTTPS, redirect to www) that strips query strings.
Detect: Use curl -I -L "https://checkoutdomain.com/cart?_gl=test" and trace the redirect chain. If any 30x hop drops ?_gl=test from the Location header, that's your culprit.
Fix: Update redirect rules to preserve query strings. In nginx: return 301 https://$host$request_uri; (note $request_uri preserves query). In Cloudflare Rules: enable "Preserve query string" on the redirect.
2. iOS 17+ Link Tracking Protection (LTP)
When users on iOS 17+ open a link from Mail, Messages, or Safari Private Browsing, the OS strips known tracking parameters — including _gl, fbclid, gclid, utm_*, and others. This affects 60%+ of US mobile users.
Detect: Segment GA4 by Operating System = iOS, then compare cross-domain attribution to Android. If iOS sessions show significantly more "direct" traffic on internal cross-domain hops, LTP is the cause.
Fix: There's no client-side fix — Apple controls this. The workaround is server-side stitching: capture the user identity via login/cookie on the originating domain, pass it server-side to the destination, and use User-ID in GA4 to reconcile sessions.
3. WAF parameter filtering (Cloudflare, AWS WAF, Akamai)
Some WAF rules strip "unknown" query parameters as a security measure (e.g., to prevent XSS via reflected query params). _gl can get caught in this.
Detect: In Cloudflare → Security → WAF → check Firewall Events for blocked or modified requests containing _gl. Or run curl -v "https://checkoutdomain.com/?_gl=test" and verify _gl=test reaches the origin server (check via your server's access logs).
Fix: Add _gl to your WAF's parameter allowlist. In Cloudflare: WAF → Managed Rules → exceptions.
4. JavaScript-based redirects
If your destination page does window.location.href = '/new-path' in JS, the query string is preserved by default — BUT if anyone wrote window.location.pathname = '/new-path', that explicitly drops query strings.
Detect: Search your codebase for location.pathname, location.replace, and history.pushState calls. Any of these that don't explicitly preserve window.location.search are suspect.
Fix: Either preserve location.search explicitly, or use location.href instead of pathname.
5. Mismatched cross-domain configuration
Both domains must list each other in their GA4 cross-domain configs. If storedomain.com is configured but checkoutdomain.com isn't (or vice versa), _gl gets generated but never consumed.
Detect: In GA4, go to Admin → Data Streams → click your stream → Configure tag settings → Configure your domains. Verify all involved domains are listed. Then check the GA4 property on each domain — they must use the same measurement ID if they're meant to be one property.
Fix: Ensure all domains are in the cross-domain list on the SAME GA4 data stream. Don't create separate properties — that defeats the purpose.
6. CDN URL rewrites and edge caching
CDNs sometimes cache responses by URL excluding query strings to maximize cache hit rate. If the CDN's cache key ignores _gl, two different users with different _gl values may receive the same cached HTML — which is fine — but the CDN may also strip the parameter before passing to the origin.
Detect: In your CDN settings, find the cache key configuration. Does it include query strings? If not, parameters may be stripped on hits to cached content.
Fix: Configure your CDN to forward all query parameters to the origin even if the cache key ignores them. In Cloudflare: Caching → Configuration → Cache Level = Standard (not "Ignore Query String").
The 5-minute audit method
- Open Chrome DevTools → Network tab → preserve log
- Start on
storedomain.com - Click a link that crosses to
checkoutdomain.com - Trace every request in the Network tab. For each redirect (status 30x), inspect the Location response header. Confirm
_glis present. - Land on the final destination. Check the current URL —
_glshould be visible. - Open the GA4 DebugView. The session_id should match across both domains. If it changes, the cross-domain stitching failed.
What "successful cross-domain" looks like in GA4
- Total Users on the storedomain.com property > storedomain.com Sessions (cross-domain users are reused)
- Sessions don't fragment when users navigate to checkoutdomain.com
- Path Exploration shows continuous flows across domains, not abrupt "drop-offs" at the boundary
- Direct/(none) traffic stays low (< 30%); a spike usually indicates cross-domain breakage
The compound problem
Each of these 6 issues alone reduces cross-domain attribution by maybe 5–15%. Stack two or three and you lose 30–50% of your true cross-domain user journeys. The data still looks reasonable in GA4 (no error messages, no zeros), which is why it goes undiagnosed for months.
Audit your cross-domain setup automatically
Cross-domain breakage is the #1 source of attribution errors in multi-property businesses. Run a free Snifflytics audit to check whether your cross-domain configuration is complete, whether _gl survives your real redirect chain, and where the iOS LTP impact is hitting hardest in your funnel.