Skip to content
accs-net.com

Press Esc to close

First-Party Cookie

A first-party cookie is a small piece of data set by the domain shown in the browser’s address bar β€” the site the user is actively visiting. When a user lands on example.com and the server (or a script on that page) issues a Set-Cookie header for example.com, the cookie is first-party. If the cookie’s domain belongs to a different site loaded inside the page, it is third-party. The distinction sounds bureaucratic, but it controls whether the cookie survives, gets blocked, or quietly degrades into a 7-day rolling token. This guide explains how the GA4 _ga cookie fits in, what ITP and ETP actually do, and why your “first-party” cookie may not be first-party at all.

A first-party cookie is set by the domain the user is browsing. The browser checks the Set-Cookie header against the address bar: if the Domain attribute matches (or is omitted, defaulting to the request host), the cookie is filed under “first-party storage” for that origin. They are the workhorse of the web β€” login state, carts, consent, and returning-visitor identity for cookie-based tracking.

Two paths lead to a first-party cookie. The server issues Set-Cookie in an HTTP response β€” server-set. Or JavaScript writes document.cookie β€” client-set. Both are first-party in browser terminology, but they age very differently under modern privacy controls.

First-party vs third-party cookies diagram
First-party cookies match the visited domain and are accepted; third-party cookies come from another domain and are blocked by Safari ITP and Firefox ETP.

First-Party vs Third-Party Cookies

The browser does not care about your business relationships, only about domain matching. A cookie whose Domain matches the registrable domain of the page is first-party; everything else is third-party.

Property First-party cookie Third-party cookie
Set-Cookie domain Matches address bar (e.g., example.com) Different domain (e.g., tracker.io)
Safari (ITP) Allowed; client-set capped at 7 days Blocked since 2020
Firefox (ETP) Allowed Blocked by default since 2019
Chrome (2026) Allowed Partitioned per top-frame (CHIPS)
Typical lifetime Server-set: up to 2 years; client-set in Safari: 7 days Often deleted on every load
Common uses Sessions, login, GA4 client_id, consent Cross-site retargeting, ad measurement

Third-party cookies are dying. Every modern browser blocks or partitions them so they cannot be linked across sites. First-party cookies remain β€” but they are squeezed too, especially when written from JavaScript.

GA4 stores its identity in two first-party cookies on your domain, both written by gtag.js β€” which makes them client-set, and that classification matters for how long they survive in Safari.

  • _ga β€” durable user identifier holding the GA4 client ID in the format GA1.2.123456789.1700000000. Default lifetime: 2 years.
  • _ga_<MEASUREMENT_ID> β€” per-stream session state. One per GA4 data stream; holds session count, start timestamp, engagement flags. Default lifetime: 2 years.

On the first visit, gtag.js generates a random client ID, writes _ga to document.cookie, and GA4 thereafter treats the user as returning. Every event includes the _ga value as cid. Lose the cookie and the next visit looks like a new user β€” “users” metric inflates and retention reports collapse.

A Set-Cookie header is more than a name and a value. The attributes determine when the cookie travels, who can read it, and how long it lives. Misconfigured attributes are the most common cause of cookies “disappearing” in production.

  • Domain β€” host the cookie is bound to. Omit it and the cookie defaults to the exact host. Set example.com and it covers all subdomains.
  • Path β€” restricts the cookie to URLs under this path. Default / covers the entire site.
  • Expires / Max-Age β€” when the cookie is deleted. Without either, it dies on tab close.
  • Secure β€” sent only over HTTPS. Required for any identity cookie.
  • HttpOnly β€” JavaScript cannot read it. Critical for session cookies; impossible for _ga because GA4’s tag needs to read it.
  • SameSite β€” controls cross-site sending. Lax (default) sends on top-level navigations. Strict only same-site. None requires Secure and is the only value allowing third-party context.
Set-Cookie: _ga=GA1.2.123456789.1700000000;
            Domain=example.com; Path=/;
            Expires=Tue, 28 Apr 2028 12:00:00 GMT;
            Secure; SameSite=Lax

ITP and ETP Impact on First-Party Cookies

Apple’s Intelligent Tracking Prevention (ITP) and Mozilla’s Enhanced Tracking Protection (ETP) reshaped what “first-party” actually means. The most important rule, from WebKit’s 2020 update, is the 7-day cap on client-set first-party cookies in Safari.

When JavaScript writes a cookie via document.cookie, Safari files it under “client-side script-writeable storage” and starts a 7-day timer from the most recent write. If the user does not return within seven days, the cookie is deleted. The timer resets on each visit β€” but every visitor with a gap longer than a week looks brand-new to GA4. Server-set cookies issued via HTTP Set-Cookie are not subject to this cap. Firefox’s ETP focuses on cross-site tracking and generally leaves first-party storage alone.

The practical consequence: in Safari traffic, your “new users” number is structurally inflated. There is no client-side workaround. The fix is server-side.

Server-Set vs Client-Set First-Party Cookies

Browsers see both as first-party β€” same domain, same scope, same access rules. ITP sees them very differently. Knowing which one you have changes the lifetime estimate by a factor of 100.

Aspect Server-set cookie Client-set cookie
Source HTTP Set-Cookie response from origin document.cookie = "..." in JS
Safari ITP cap Not capped at 7 days Capped at 7 days from last write
HttpOnly possible Yes β€” recommended for sessions No β€” must be readable from JS
GA4 _ga Set by server-side GTM if used Set by gtag.js by default
Implementation cost Higher β€” needs sGTM or backend route Zero β€” drop in the snippet

This is the engine behind every “first-party data infrastructure” pitch. Move the cookie write from gtag.js to a backend response and Safari treats it as a serious cookie instead of a 7-day token.

By default GA4’s tag is JavaScript and writes cookies from document.cookie. Fine for Chrome and Firefox traffic, but every Safari user is on a 7-day clock. Google’s guidance recommends server-side tagging as the mitigation.

Server-side GTM (sGTM) proxies GA4’s hit collection through your own subdomain. Instead of sending events to www.google-analytics.com, the page sends them to tagging.example.com β€” a Cloud Run container running the sGTM image. The response includes a Set-Cookie for _ga on example.com; Safari’s 7-day cap no longer applies. Three details matter:

  1. The tagging endpoint must be a subdomain of the main site, not a third-party host.
  2. The cookie must use Domain=example.com so all subdomains can read it.
  3. SSL must cover the subdomain β€” sGTM will not function over plain HTTP.

You also gain debug visibility: DebugView shows the exact request the server forwards, instead of minified g/collect calls in DevTools.

Custom Domain / CNAME for First-Party Context

The CNAME setup is the budget version of sGTM. Point a subdomain (often analytics.example.com) at the analytics provider’s host via DNS CNAME β€” any Set-Cookie response is then issued for your registrable domain. Caveats: browsers fingerprint the IP and may still classify the host as third-party if the CNAME target is owned by a known tracker; you do not control the response; and you cannot use the Measurement Protocol the same way. For GA4 specifically the CNAME trick has limited utility because the GA4 collection endpoint is a Google domain. Server-side GTM with your own container is the supported path Google documents.

Privacy regulations like GDPR and the ePrivacy Directive treat analytics cookies as non-essential β€” they cannot be set before consent. Consent Mode v2 is the modern mechanism: the tag fires in a “denied” state that suppresses cookies and sends only modeled, cookieless pings. Three rules I follow:

  1. Default deny. Set analytics_storage=denied and ad_storage=denied on page load; update to granted only after the user accepts.
  2. Block before, not after. The consent gate must execute before gtag.js writes cookies. Deleting after the fact violates the spirit of consent.
  3. Document the lifetime. Your privacy policy should list every cookie GA4 sets, its lifetime, and its purpose β€” the _ga, _ga_*, and _gid entries.

I have debugged this scenario many times: a client swears their cookies are first-party, but Safari still shows a renewed client ID every visit. The domain looks correct, yet ITP treats it as ephemeral. The cause is almost always one of five things.

  1. Client-set under the hood. The Domain matches, but the cookie was written by JavaScript, not an HTTP response. The 7-day cap fires regardless. Confirm in DevTools β†’ Network β†’ Headers β€” no Set-Cookie in the response means it was written client-side.
  2. Wrong subdomain on the tagging server. Your sGTM container is at track-prod-xyz.a.run.app, not tagging.example.com. A CNAME-mapped subdomain is required.
  3. Mixed protocols. The page is HTTPS but the cookie request goes to HTTP. Safari rejects silently.
  4. SameSite=None without Secure. Browsers reject the cookie outright. One attribute, but the symptom looks like ITP.
  5. Bounce-tracking heuristics. Safari’s Link Decoration caps cookies at 24 hours if it detects bounce-tracking β€” even on your own domain. Avoid query-string IDs in cross-domain redirects.

Fastest diagnostic: open DevTools β†’ Application β†’ Cookies β†’ your domain. Right-click _ga β†’ “Show request that set this cookie.” If the answer is “No request β€” set by JavaScript,” you have a client-set cookie. Combine GA4 with cross-device tracking via Google Signals to soften the data damage while migrating to server-side cookie writes.

Bottom Line

A first-party cookie is set by the visited domain, but how it was set β€” server header or JavaScript β€” decides whether Safari treats it as a 2-year identifier or a 7-day token. GA4’s _ga is first-party in name but client-set by default, which is why server-side GTM is now the standard answer for accurate user counts in privacy-strict browsers. Get the Domain, Secure, SameSite, and consent gating right, and your first-party cookies will actually be first-party.

Frequently Asked Questions

What is a first-party cookie?

A first-party cookie is set by the domain in the browser’s address bar β€” the Set-Cookie response (or JS writing document.cookie) specifies a Domain matching the visited site. Examples: session cookies, login state, GA4’s _ga, consent preferences.

Is the GA4 _ga cookie first-party?

Yes β€” _ga is set with Domain=example.com on your site. But by default gtag.js writes it from JavaScript, so Safari’s ITP caps it at 7 days from the last visit. Server-side GTM removes the cap by writing the cookie via an HTTP response.

How long does a first-party cookie last in Safari?

Server-set cookies follow Expires or Max-Age (up to 2 years for GA4). Client-set first-party cookies are capped at 7 days from the most recent write under ITP. The cap resets on each visit but deletes the cookie if the user does not return within a week.

First-party vs third-party cookies β€” what is the difference?

First-party cookies are issued by the domain in the address bar; third-party cookies come from other domains loaded inside the page. Modern browsers block or partition third-party cookies but allow first-party cookies, which is why first-party identity is the foundation of accurate analytics.

Can JavaScript read first-party cookies?

Only if the cookie is not flagged HttpOnly. JS reads cookies via document.cookie, which excludes HttpOnly entries. The GA4 _ga cannot be HttpOnly because the analytics tag must read it to keep the client ID stable across pages.

Do first-party cookies require user consent under GDPR?

Yes for non-essential cookies β€” analytics cookies like _ga require opt-in consent under GDPR and the ePrivacy Directive. Strictly necessary cookies (session, security, consent state) do not. Use Consent Mode v2 to gate GA4 cookies.

How does server-side GTM make cookies first-party?

sGTM proxies analytics requests through a subdomain you own, like tagging.example.com. The container responds with Set-Cookie bound to your domain, which the browser stores as server-set first-party. Safari does not apply the 7-day cap, so the cookie lives the full Expires lifetime.

External references: MDN β€” HTTP cookies, web.dev β€” SameSite cookies explained, Google β€” GA4 cookies and identifiers.

Tom Martin
Written by

Tom Martin

Web analytics specialist with deep expertise in Google Analytics, Tag Manager, and e-commerce tracking. Helping businesses understand their data without the noise β€” practical guides, honest reviews, and real-world implementation experience.