Attribution

Short answer: Safari limits JavaScript cookies to seven days and many iOS users opt out of tracking, so collect your own attribution data on every session before anyone converts. Store it in cookies set by your own server, which last longer, and pass it through hidden fields on every form so it reaches the CRM contact record.

First-party data as the attribution foundation, how to collect it before you need it, cover

Every meaningful attribution conversation eventually hits the same wall: the data you need to answer a question was never collected. Safari's Intelligent Tracking Prevention limits cookie lifetimes to seven days for cookies set via JavaScript. iOS ATT's global opt-in rate has sat around 25% since Apple introduced the framework. Third-party signals are shrinking. First-party data is the answer, but only if you start collecting it before you discover you need it.

What first-party attribution data actually is

First-party data for attribution is not the same as first-party data for personalisation or CRM enrichment. For attribution purposes, the specific data you need is narrow: how did this user first arrive at your site, what source and campaign brought them, what is the persistent identifier that ties their sessions together across visits, and how does that identifier connect to the CRM contact record created when they converted. This is the causal chain from the first session to the conversion event. Without it, you know that someone became a lead. You do not know what caused them to become a lead, which is the only question that determines where next month's acquisition budget should go.

Three data points to capture on every session

Before any user converts, you have one window to collect attribution data: the session itself. Three specific data points create the foundation. First: the GA4 client_id from the _ga cookie, the persistent identifier GA4 uses to stitch sessions together over time. Read it via JavaScript and store it as a data layer variable. Second: the first-touch UTM parameters. On the user's first session (before your attribution cookie exists), read utm_source, utm_medium, and utm_campaign from the URL and write them to a first-party cookie with a 90-day expiry. Critically: never overwrite this cookie on return visits. The first touch is what you want to preserve. Third: the full landing page URL with all query parameters intact, captured at session start. These three data points cost nothing beyond an afternoon of GTM configuration.

The cookie architecture that survives Safari ITP

Safari ITP limits cookies written via document.cookie (JavaScript-set cookies) to seven days. Cookies set via HTTP response headers by your own server are not subject to the same restriction. The practical implication: if your attribution cookie is set by a JavaScript snippet in GTM, it expires in seven days for Safari users, which represent a substantial share of mobile traffic. The correct architecture is server-set cookies. When a user arrives with UTM parameters, your server reads the query string from the incoming request and sets the attribution cookie in the HTTP Set-Cookie response header. GTM server-side containers can do this: configure a server-side tag to read incoming event parameters and emit a Set-Cookie header in the response. The attribution cookie then behaves identically across browsers and survives ITP.

Connecting session data to your CRM contact record

All of the session-level attribution data above is wasted if it never reaches the CRM. The connection point is the form submission. Every form on your site, demo request, contact, newsletter, any form, should include hidden fields that are populated by JavaScript immediately before the form submits. Those hidden fields carry the GA4 client_id, the first-touch cookie values, and the current page URL. When the form submits, these fields travel through the webhook or CRM native integration and populate dedicated custom fields on the contact record. Two implementation rules matter here: the fields should be write-once, meaning a second form submission from the same contact should not overwrite the original first-touch attribution, and the fields should be required in the form handler, so submissions that fail to capture these values trigger an error you can investigate.

FAQ

Three data points: the GA4 client_id from the _ga cookie, which stitches sessions together; the first-touch utm_source, utm_medium and utm_campaign, written to a first-party cookie with a 90-day expiry and never overwritten; and the full landing page URL with query parameters intact. It takes about an afternoon of GTM configuration.

Safari ITP limits cookies set via JavaScript to seven days, but cookies set by your own server in the HTTP response header are not restricted the same way. Have your server read the UTM query string and set the attribution cookie through Set-Cookie. A GTM server-side container can do this, so the cookie survives ITP.

Add hidden fields to every form, populated by JavaScript just before submission, carrying the GA4 client_id, first-touch cookie values and current page URL. They flow through the webhook or native integration into custom contact fields. Make the fields write-once so later submissions do not overwrite first touch, and required so capture failures trigger an error.

Read this article on your favourite platform

Ready to build the system?

If this describes your funnel, a 30-minute call will find where the constraint sits in your own numbers and what it would take to fix it.

It starts with a 30-minute call. Pick a time below.

Prefer email?