Skip to content

Consent-based GTM setup for generating and persisting a long-lived, secure user ID cookie — ITP/ETP-compliant, GA4-ready, and fully privacy-aware.

License

Notifications You must be signed in to change notification settings

mediafaktur/gtm-custom-user-id-cookie

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GTM Custom User ID Cookie (CUIC)

Consent-based, long-lived first-party ID cookie for privacy-friendly user tracking in restrictive environments.

Overview

Custom User ID Cookie (CUIC) is a solution specifically designed for tracking setups built on Google Tag Manager (GTM). It enables the generation and persistence of a random user ID across sessions via a secure, first-party HTTP-only cookie. The ID is generated client-side in GTM (based on user consent, if applicable) and made available immediately — even before the cookie is set — for GA4 tracking and other downstream integrations.

CUIC is fully compatible with ITP/ETP restrictions and enables stable cross-session identification in Safari and Firefox. It supports the use of user_id in both client- and server-side tracking architectures.

All configuration of key behaviors — such as cookie name, expiration, refresh policy, and visibility — is managed directly in GTM. No changes to the PHP files are required during setup, maintenance, or testing.

Key Features

  • Persistent first-party, secure, HTTP-only cookie (SameSite=Lax)
  • Optional JS-readable mirror cookie for client-side JavaScript access
  • ID available on first pageview – usable before the cookie is stored
  • Fully consent-controllable via GTM
  • Compatible with Safari/Firefox ITP/ETP restrictions
  • HTTP-only cookie value accessible via GTM data layer
  • Entire behavior (refresh, name, expiration, mirror cookie) controlled via GTM
  • Optional fallback via if XHR is blocked
  • Ideal for GA4 user_id and server-side tracking setups

What Problem Does CUIC Solve?

🔍 Problem ✅ CUIC Solution
1st-party, long-lived cookie required, but:
only valid if set server-side with secure, same-origin conditions
CUIC sets the cookie server-side on the main domain using PHP — not via JavaScript
Consent is required before setting any ID, including via server CUIC integrates seamlessly with GTM consent triggers, ensuring privacy compliance
GA4 and server must use the same ID immediately, even before the cookie exists CUIC uses a central variable (jsUserIdResolve) to generate or reuse the ID and send it simultaneously to GA4 and the cookie handler
On subsequent pageviews, the HttpOnly cookie is not accessible via JS, and the ID must still be available in GTM CUIC exposes the cookie value via a PHP dataLayer snippet (cuic_datalayer-snippet.php), making the ID available before GTM loads
Multiple entry points / pageviews must not generate multiple IDs CUIC ensures consistent logic in both GTM and PHP: existing values are always reused if present (in dataLayer or cookies)
Safari and Firefox block or shorten JS-set cookies CUIC fully complies with Safari/Firefox ITP: 1st party, secure, SameSite=Lax, server-set, HttpOnly
XHR requests may fail due to ad blockers or ITP CUIC includes an automatic fallback using an image GET request if the main XHR fails

Architecture & Data Flow

The diagram below illustrates the CUIC cookie lifecycle across first and subsequent pageviews:

CUIC Architecture & Data Flow

Components

  • cuic_controller.html
    GTM Custom HTML tag that sends the ID to the server-side cookie handler – includes XHR logic with fallback image request
    Triggers cookie creation/update after consent; configurable via GTM variables

  • cuic_cookie-handler.php
    Server-side handler that sets the cookie via POST or GET – supports dynamic config via GTM
    Used by the controller tag to persist the ID as a secure, SameSite, HTTP-only cookie

  • cuic_datalayer-snippet.php
    PHP snippet which exposes the cookie value to the dataLayer via PHP in non-cached environments – required when using HttpOnly cookies
    Ensures ID is available on first pageload

  • cuic_datalayer-snippet_cached.php
    JavaScript-based alternative for use on fully cached pages (e.g. WordPress with page caching)
    Loaded via <script src="/...">; executes server-side, delivers JS response

  • jsUserIdResolve.js
    Central GTM variable that checks for an existing value in the dataLayer (from cookie snippet or previous tag push) or creates a new ID on first pageview
    Ensures consistent value at all times

Installation Guide

  1. Deploy the server scripts to your main domain (same origin as your GTM container):

    • /cuic_cookie-handler.php
      → receives the Custom User ID and sets the tkncstm cookie (via POST or GET)

    • /cuic_datalayer-snippet.php
      → for non-cached environments
      → inject directly into the HTML <head> via PHP, before the GTM container
      → ensures the Custom User ID is available synchronously at page render time
      or alternatively:
    • /cuic_datalayer-snippet_cached.php
      → for cached or static environments (e.g. full-page caching, CDNs)
      → embed via <script src="/cuic_datalayer-snippet_cached.php"> in the <head>
      → returns a dynamic JavaScript response with the dataLayer push, bypassing HTML caching
  2. Create GTM variables:

    • Add jsUserIdResolve.js as a Custom JavaScript Variable
    • Name it jsUserIdResolved or similar
  3. Add the GTM tag:

    • Use cuic_controller.html as a Custom HTML Tag
    • Adjust path to /cuic_cookie-handler.php if needed
    • Reference the jsUserIdResolved variable in the tag
    • Trigger only after valid consent
  4. Make the ID available to GA4:

    • Use {{jsUserIdResolved}} as user_id in your GA4 Config and Events
  5. Customize behavior via GTM:

    • All cookie parameters (name, refresh, maxAge, httpOnly) are configurable via inline variables in the tag
    • Optional: Enable JS-readable mirror cookie by setting addJsCookie = "1" (see below)
    • No PHP edits required

Consent & Privacy

  • CUIC should only run after user consent (fully controlled via GTM)
  • No personally identifiable information (PII) is stored
  • No fingerprinting, no localStorage
  • Fully compliant with ITP, ETP, GDPR, and ePrivacy standards

Use Case: Testing Multiple Cookie Variants

CUIC makes it easy to test different cookie configurations (e.g. name, expiration, visibility, refresh behavior) without modifying any server-side code. Just duplicate the GTM tag (cuic_controller.html) and adjust the inline config:

  • cn: cookie name (e.g. tkncstm_test)
  • refresh: 1 = extend on every visit, 0 = set once
  • httpOnly: 1 = HTTP-only (default), 0 = JS-readable
  • maxAge: cookie lifetime in seconds (e.g. 3600 for 1 hour)
  • addJsCookie: 1 = enable JS-readable mirror cookie, 0 = disabled (default)
  • jsCookieName: name for the mirror cookie (default: tkncstm_m)

You can run multiple variants in parallel using different GTM tags and triggers — all handled by the same cuic_cookie-handler.php on the server.

Mirror Cookie Feature

CUIC optionally supports a JS-readable mirror cookie that runs alongside the primary HTTP-only cookie. This enables client-side JavaScript access to the user ID when needed (e.g., for custom analytics, A/B testing, or client-side personalization) while maintaining the security benefits of an HTTP-only primary cookie.

How it works:

  • When addJsCookie = "1" is set, both cookies are created together on the first page load
  • The mirror cookie always mirrors the value of the primary cookie
  • If the primary cookie exists, the mirror cookie synchronizes with it
  • Both cookies share the same expiry timestamp (prevents drift)
  • The mirror cookie is not HTTP-only, allowing JavaScript to read it via document.cookie

Use cases:

  • Stape Cookie Keeper: Enables Stape's cookie synchronization to access the user ID via JavaScript for cross-domain and cross-platform consistency
  • Client-side JavaScript needs access to the user ID
  • A/B testing frameworks that require cookie-based user identification
  • Custom analytics scripts that run before GTM loads
  • Maintaining backward compatibility with legacy JavaScript code

Configuration: In cuic_controller.html, set:

var addJsCookie = "1";               // Enable mirror cookie
var jsCookieName = "tkncstm_m";      // Mirror cookie name (optional, defaults to "tkncstm_m")

The mirror cookie will automatically:

  • Be created alongside the primary cookie on first load
  • Stay synchronized with the primary cookie value on subsequent pageviews
  • Share the same expiration time as the primary cookie

License

MIT – see LICENSE

Author

/ MEDIAFAKTUR – Marketing Performance Precision, https://mediafaktur.marketing
Florian Pankarter, [email protected]