Skip to content
ceaksan
PREMIUM analytics

GA4 Source/Medium Debugging: Detecting the Real Source with First-Party Cookies

First-party cookie based source tagging implementation to detect the real origin of purchase events showing as (direct)/(none) in GA4. GTM Custom HTML tags, cookie structure, GA4 custom dimension setup, and debug tools.

Apr 18, 2026
TL;DR

GA4's attribution model can lose source information on purchase events due to payment redirects, Safari ITP, and in-app browsers. The first-party cookie solution in this article writes UTM/gclid/fbclid information to two cookies on the landing page (dnm_src and dnm_cid), then sends it to GA4 as an event-scope custom dimension at purchase. Without touching the GA4 attribution model, it lets you see the real source as a secondary dimension in reports.

Membership Required

You need to sign in and have a Premium subscription to access this content.

Key Takeaways
  • 01 Two-cookie architecture: dnm_src (source|medium|campaign|content|term) and dnm_cid (click ID type and value), 30-day TTL
  • 02 Overwrite logic: when a cookie exists, it only overwrites on a new click ID (gclid/fbclid); organic return visits don't wipe the previous paid source
  • 03 3 GA4 custom dimensions: first_touch_source, first_touch_campaign, first_touch_click_id, all event scope
  • 04 Custom dimensions are not included in the attribution model; they are only used as secondary dimensions in reports
  • 05 Safari ITP limits JS cookies to 7 days and localStorage to 7 days of inactivity. A complete fix requires server-side cookie setting (sGTM)
Frequently Asked Questions (FAQ)
+ Does this solution change GA4's attribution model?

No. Data written to the cookie is sent to GA4 as an event-scope custom dimension. It is not included in the calculation of DDA, last-click, or other attribution models. It is only used as a secondary dimension in Explore reports.

+ Are cookies written when consent is missing?

They shouldn't be. Add an analytics_storage: granted condition to the source capture tag's trigger in GTM. Cookies are not set unless the user gives consent.

+ Does the 30-day cookie lifetime work in Safari?

No. Safari ITP limits cookies created via JavaScript to 7 days. With gclid/fbclid in the URL, this drops to 24 hours. A full solution requires HTTP-only cookies set via server-side GTM.

+ Why use two separate cookies instead of a single one?

A single JSON cookie approach risks hitting the 4KB cookie size limit. Splitting source/medium/campaign and click ID provides size safety and simplifies parsing.

+ Is the cookie overwritten on return visits?

Only if a new click ID (gclid or fbclid) is present in the URL. Organic or direct return visits without parameters preserve the previous paid source information.

+ Which e-commerce platforms does this solution work on?

Any platform where GTM can be loaded: Shopify, T-Soft, Ticimax, Ikas, WooCommerce, custom platforms. It is a platform-independent solution.

+ I finished the implementation but custom dimension data doesn't appear in GA4 reports. Why?

GA4 standard reports have a 24-48 hour processing delay. A custom dimension starts collecting data from the moment it is defined; it is not backfilled. Use DebugView for instant checks in the first 48 hours, and evaluate accumulated data in Explore reports after 2-3 days.

+ Does the localStorage fallback also protect against Safari?

Partially. After Safari ITP 2.3, localStorage is also cleared after 7 days of inactivity. The storageHelper dual-write recovers cookies wiped by browser extensions or manual clearing, but in Safari both cookie and localStorage share the same 7-day limit.