Blog
Three identifiers that GA4 uses to stitch sessions together. Overriding them server-side fixes problems that the browser cannot.
GA4 uses three identifiers to attribute events to users: client_id (cookie-based, browser-scoped), session_id (per-session, derived from client_id), and user_id (your own cross-device identifier). All three can be overridden server-side, and each override solves a specific class of problem.
Default: GA4 cookie value, generated client-side. Server-side override: read from a first-party HttpOnly cookie set by your tagging server. The override means client_id survives ITP-driven cookie deletion (the GA4 cookie is JavaScript-set and gets deleted; the HttpOnly one does not).
// In your GA4 tag, override client_id
const persistent = getCookieValues('_user_id')[0];
return persistent || getEventData('client_id');
Default: GA4 generates this from the cookie, with a 30-minute inactivity timeout. Server-side override: useful if you need to enforce different session boundaries (for example, a session-per-checkout-attempt for ecommerce reporting).
Less commonly overridden than client_id. The defaults work for most use cases, and changing them affects how GA4 reports sessions in unexpected ways.
Default: not set (you have to provide it explicitly). Server-side override: pull from your authentication system once the user is logged in. Without user_id, GA4 cannot stitch a single user across devices, even if other signals (email hash) are present.
// In your GA4 tag, set user_id
const sessionUserId = getCookieValues('_auth_user_id')[0];
if (sessionUserId) return sessionUserId;
return undefined; // unset for unauthenticated users
| Problem | Override |
|---|---|
| Users appear as new on every visit | client_id from HttpOnly cookie |
| Cross-device journeys broken | user_id from auth system |
| Sessions splitting too aggressively | session_id (rare, careful) |
| First-time visit needs to be remembered after consent | client_id propagation from before consent |
If your privacy posture is "no cross-device identity," do not set user_id. The two cookie-derived IDs are session/device-scoped and do not survive across devices. user_id is what crosses the boundary; leaving it unset keeps your tracking strictly per-device.