Tracking accuracy

Cross-domain tracking: checkouts, booking tools and iframes

Why a visitor who moves to a checkout, a booking tool or an embedded form loses their source, and how to keep the credit with the ad that brought them.

On this page

A visitor clicks your ad, lands on your site, then pays on checkout.stripe.com, books on calendly.com, or fills in a form that lives inside an iframe from another company. At that moment the journey crosses a domain line. Browsers keep cookies and storage per site, so on the other side the visitor is a stranger, and on the way back your analytics may think they arrived from the checkout.

This guide covers the three cases that break attribution most often, and the fix for each. It is one of the causes behind analytics numbers that never match.

What breaks, exactly

Two separate things go wrong:

  1. Identity. A first-party cookie set on yourshop.com is not readable on book.example.net. A tool running on both sides sees two visitors.
  2. Source. When the buyer returns from the payment page, the referrer is the payment page. Without a rule, the return trip starts a new visit "from paypal.com", and the ad that brought the buyer loses the sale.

Subdomains are different from separate domains. A cookie set for yourshop.com can be shared by www.yourshop.com and shop.yourshop.com. Two different domains, such as yourshop.com and yourshop-store.com, can never share one.

Case 1: hosted checkouts and payment pages

Stripe Checkout, PayPal, Klarna and similar services take the buyer to their own domain and send them back to your thank-you page.

Fix the source. Tell your analytics the payment domain is part of the journey, not a referral. GA4 calls this the unwanted referrals list: matching domains get ignore_referrer=true on their events, so the session keeps its original source. Google names third-party payment processors as the main use (Google).

Fix the sale. The thank-you page can fail: buyers close the tab before it loads, and some payment methods confirm later. Count the sale from the server too, with a shared order id so it is counted once.

With Stripe specifically:

  • Put {CHECKOUT_SESSION_ID} in your success URL. Stripe replaces it with the real session id after payment, so your thank-you page knows which order it is (Stripe). Use that id as the event id in the browser and on the server.
  • Stripe recommends fulfilling from the checkout.session.completed webhook, not the redirect alone, because a buyer can pay and lose connection before the page loads (Stripe).
  • Payment Links can redirect to your own page too, and UTM tags on the payment link are added to the redirect address (Stripe).

Case 2: two domains you own

A brand site on one domain and a shop or app on another is common. Here you want one visitor across both.

GA4 does this with cross-domain measurement: you list the domains in the data stream, and GA4 adds a _gl parameter to links between them, which carries the visitor's id across (Google). It only works when people move by clicking a link. Someone who types the second address starts fresh. A redirect that drops the query string drops _gl with it.

The simplest fix is structural: put the second property on a subdomain of the first, such as shop.yourbrand.com, and the problem goes away for any tool that sets its cookie on the parent domain.

Case 3: iframes and embedded tools

Booking widgets, form builders and some checkouts are embedded in your page as an iframe from another domain. Code inside that iframe runs in a third-party context:

  • Safari blocks third-party cookies by default, and has since 2020 (WebKit).
  • Firefox gives third-party cookies a separate jar per site by default, with Total Cookie Protection (MDN).
  • Chrome does not block them by default, so a setup that works in Chrome can fail in Safari and Firefox (MDN).

So do not track inside the iframe. Track on your own page, where your analytics already knows the visitor. Many embedded tools tell the parent page what happened through postMessage. Calendly's embed, for example, posts events such as calendly.event_scheduled to the parent window (Calendly). Listen for that event, check its origin, and count the conversion on your page.

If the tool sends no events, count what happens after: a redirect to your own thank-you page, or a webhook from the tool to your server.

A worked example

Example numbers, made up for illustration. A course creator sells through Stripe Checkout and finds that checkout.stripe.com is her third biggest source of sales, with 31 of 140 sales in a month.

  1. The cause. Buyers return from Stripe to /thanks, and the referrer is Stripe. Those visits start fresh.
  2. Fix the source. She adds checkout.stripe.com to the payment hosts her analytics treats as her own site.
  3. Fix the sale. Her success URL becomes /thanks?session_id={CHECKOUT_SESSION_ID}, and the thank-you page sends the purchase with the session id as its event id. Her server sends the same id from the webhook.
  4. The result. Stripe drops out of the sources. The 31 sales move back to the ads and emails that brought those buyers, and none is counted twice.

Common mistakes

  1. Adding your own domain to the referral list as a fix for everything. It hides the symptom; find the page that loses the tag.
  2. Tracking inside an iframe and trusting the numbers from Chrome only.
  3. Relying on the thank-you page alone for sales.
  4. Different event ids in the browser and on the server, so the same order counts twice.
  5. A redirect that drops parameters between your domains.

How Sporetag handles it

  • Payment pages. Sporetag knows common payment hosts such as PayPal, Stripe, Shopify checkout, Klarna and Square. Its tracking health check spots a payment page or your own domain starting visits, and Count them as my site fixes it in one tap. The visit carries on and the credit stays with the source that brought the buyer.
  • Subdomains. A site counts visits from its domain and every subdomain. In 90-day tracking, the first-party id is set on the parent domain, so shop. and www. share it.
  • Stripe Checkout. Copy the _st_id cookie into the Checkout Session's metadata.st_id, and the Stripe connector ties each sale to the visits before it. The connector uses the session id as the event id, so a browser event with the same id counts once.
  • Embedded Calendly. With the "Someone books or calls" goal on, a booking made inside an embedded Calendly widget counts.
  • Two separate domains. Sporetag has no link decorator between different domains today. Each domain is its own site, and a person moving between them is two visitors.

Common questions

What is cross-domain tracking?

Keeping one visitor and one source as someone moves between two domains, such as your site and a checkout or booking tool. Without it, the second domain sees a new visitor, and the return trip can look like a referral.

Why is PayPal or Stripe showing as a referral source?

Buyers come back from the payment page to your thank-you page, so the referrer is the payment page. List those domains as part of your site in your analytics, so the visit keeps its original source.

Do I need cross-domain tracking for subdomains?

Usually not. A tool that sets its cookie on the parent domain shares it across subdomains. You need it for different domains.

Can I track conversions inside an iframe?

Not reliably. Safari and Firefox block or partition the cookies an iframe from another domain needs. Count the conversion on your own page, from an event the embedded tool posts, or from a redirect or webhook.

Does cross-domain tracking work if people type the address?

No. Methods like GA4's _gl parameter travel in the link. A typed address or a bookmark starts fresh.

Sources

Spotted something wrong or out of date? Email hello@sporetag.com and we will fix it.