Cross-Domain Session Attribution in GA4: Fixing Linker Configuration Across Two E-commerce Properties
When you operate multiple domains sharing a single Google Analytics 4 property, session continuity breaks without proper cross-domain linker configuration. This post documents the audit and remediation of GA4 tracking across queenofsandiego.com and sailjada.com—two separate booking platforms unified under one analytics property (G-N6HKL4KLKT)—and how a missing configuration parameter caused ~60 pages to lose conversion attribution.
The Problem: Silent Session Breaks
Both domains share a single GA4 property and drive traffic to each other: QOS event pages link to sailjada.com for bookings, and sailjada.com links back to QOS for charter details. Without cross-domain linker configuration, clicking from QOS → sailjada.com creates a new session in GA, breaking the conversion funnel attribution chain.
The audit revealed:
- sailjada.com: All 22 public pages correctly implemented GA + linker (clean baseline)
- queenofsandiego.com:
- ~60 pages had GA tag but missing linker config
- 3 pages entirely missing GA (charters detail pages, thank-you page)
Root cause: The QOS gtag configuration was missing the allow_google_signals and cross-domain linker object that tells GA4 to append a tracking parameter to outbound links.
Audit Methodology: Systematic Cross-Domain Verification
The audit proceeded in three phases:
Phase 1: Inventory
Extracted all unique GA snippet patterns from both properties across local and S3-deployed files:
# Find all gtag config calls with unique patterns
grep -r "gtag('config'" /path/to/repos/sites/queenofsandiego.com | \
sed "s/.*gtag('config'/gtag('config'/" | sort -u
# Check placement (head vs body) and linker presence
grep -B5 "gtag('config'" index.html | grep -E "(head|body|linker)"
Phase 2: Linker Configuration Audit
For each GA tag, verified two critical properties:
- Presence of
linker: { domains: ['queenofsandiego.com', 'sailjada.com'] } - Correct tag placement in
<head>(not deferred to body, which breaks linker)
Phase 3: Event Tracking Depth
Mapped conversion events across the booking funnel:
cta_click → generate_lead → begin_checkout → purchase
Found: Core events firing correctly, but missing view_item on charter detail pages and no video engagement tracking on hero sections.
The Fix: Standardized gtag Configuration
Created a unified configuration template and applied it across all QOS pages. The corrected config object:
gtag('config', 'G-N6HKL4KLKT', {
'page_path': window.location.pathname,
'linker': {
'domains': ['queenofsandiego.com', 'sailjada.com']
},
'allow_google_signals': true,
'allow_ad_personalization_signals': true
});
Key additions:
linker: { domains: [...] }— Enables automatic URL parameter injection on cross-domain linksallow_google_signals: true— Permits GA to use Google signals for cross-device attribution (requires user consent in most jurisdictions)page_path— Ensures SPA and canonical URL handling (important for Rady Shell event pages served from single index.html)
Files Modified:
/Users/cb/Documents/repos/sites/queenofsandiego.com/index.html(5 edits to cover apex + all event page variants)/Users/cb/Documents/repos/sites/queenofsandiego.com/thank-you/index.html(GA tag added)/Users/cb/Documents/repos/sites/queenofsandiego.com/charters/*/index.html(GA + linker added via script generation)
For the 60+ pages without manual edits, a bulk remediation script was created (/tmp/fix_ga.py) that:
# Pseudo-code for automated fix
for each_html_file in qos_pages:
if 'gtag' in file and 'linker' not in file:
inject_linker_config(file)
elif 'gtag' not in file:
add_full_ga_snippet(file)
sync_to_s3(updated_files, bucket='queenofsandiego.com')
invalidate_cloudfront(dist_id='E2MLBZVR48STAG')
Infrastructure: CDN and Deployment
S3 Buckets:
queenofsandiego.com— Apex domain contentwww.queenofsandiego.com— Subdomain alias (redirect via CloudFront function)shell.queenofsandiego.com— Rady Shell event ticketing subdomainsailjada.com— Charter booking platformwww.sailjada.com— Alias
CloudFront Distributions:
E2MLBZVR48STAG— Serves www.queenofsandiego.com (aliases: queenofsandiego.com, shell.queenofsandiego.com)- Separate distribution for sailjada.com apex
Cache Invalidation:
After deploying HTML changes to S3, CloudFront cache TTL required invalidation for all modified paths:
aws cloudfront create-invalidation \
--distribution-id E2MLBZVR48STAG \
--paths "/*"
This was critical—without invalidation, users would receive stale HTML missing the linker config.
Bonus: Fixing a Typo Domain
During audit, discovered salejada.com (typo) was registered and ranking in search. Created a CloudFront Function to redirect all traffic to the correct domain:
// CloudFront Function at edge
if (request.headers.host.value === 'salejada.com') {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
'location': { value: 'https://sailjada.com'