Server-Set Cookies with Cloudflare and ssGTM: Consent and ITP Limits

Contents
A Cloudflare Worker can set cookies through HTTP responses and forward measurement requests to a server container. This provides control over data paths and cookie attributes. It does not guarantee a bypass of Safari’s Intelligent Tracking Prevention (ITP), a fixed retention period or legal compliance.
What ITP actually limits
WebKit describes deletion of script-writable storage after seven days of Safari use without interaction with the website. This counts Safari usage days, not simply seven calendar days. Detected link decoration can impose a 24-hour limit on cookies created by JavaScript on the landing page. Not every URL parameter meets those detection conditions.
HTTP cookies can also fall under a seven-day cap when WebKit detects third-party CNAME or IP cloaking. The specific relationship to the main page matters; CDN use alone is not the criterion. IP detection is heuristic and can change. A matching hostname or consent does not grant an exception from browser rules.
Max-Age specifies a requested lifetime. Browser limits, private sessions, cleanup and manual deletion can shorten actual storage. If a missing identifier causes the server to generate a new UUID, that creates a new identifier rather than restoring the previous identity.

Cookie attributes without false guarantees
| Attribute | Effect and limit |
|---|---|
HttpOnly |
Blocks direct JavaScript access to the cookie value. XSS can still perform actions in the page context; there is no ITP immunity. |
Secure |
Restricts cookie transmission to secure connections. The Worker-to-backend connection also needs suitable transport configuration. |
SameSite=Lax |
Controls sending on cross-site requests. It does not turn a vendor into the website’s own organization. |
No Domain |
Creates a host-only cookie. A Domain attribute expands scope to matching subdomains and does not prevent CNAME detection. |
Path=/ |
Covers all paths on the matching host; a cookie path is not a reliable security boundary. |
An explicit cookie lifecycle
For an analytics identifier requiring consent, permission must be checked before creation and the associated processing. A freely supplied request parameter containing “granted” is not reliable evidence of consent. The actual CMP integration needs to account for the current choice, purpose and withdrawal.
- A dedicated endpoint accepts only its intended request type and verified origin; state changes require suitable protection against externally induced calls.
- Without valid permission, no new analytics identifier is created. Withdrawal deletes the existing cookie with matching scope and stops further use.
- With permission, an existing valid value can be reused. A missing value leads to a fresh random identifier only when creation is allowed.
- The personalized response explicitly disables caching. Shared page caches must not reuse identifiers from visitor-specific responses.
- The server client and every destination tag need a documented mapping covering consent, missing identifiers and withdrawal.
The following headers describe a new host-only cookie with an illustrative 30-day lifetime. The placeholder requires replacement with a securely generated random value. The example contains no CMP verification, endpoint or proxy implementation and is not a deployable Worker.
Cache-Control: private, no-store
Set-Cookie: __Host-fp_id=RANDOM_IDENTIFIER; Max-Age=2592000; Path=/; Secure; HttpOnly; SameSite=Lax
In supporting browsers, the __Host- prefix requires Secure, Path=/ and no Domain. Withdrawal on the same host uses:
Cache-Control: private, no-store
Set-Cookie: __Host-fp_id=; Max-Age=0; Path=/; Secure; HttpOnly; SameSite=Lax
Existing cookies with different names, domains or paths require their own matching deletions. The next request after withdrawal must not immediately create another identifier. Actual browser storage and outgoing requests provide stronger evidence than the deletion header alone.
The first request does not contain the new cookie
With forwarding through fetch(request), the backend request is already underway before an added Set-Cookie response reaches the browser. A newly set response cookie therefore does not retroactively appear in the first backend request. The integration must explicitly handle that sequence rather than assume automatic mapping.
A custom fp_id or __Host-fp_id is also not automatically a GA4 or Google Ads client ID. The responsible server client, destination schemas and effects on user and session counts need verification. A parallel identifier can change the existing identity model.
Google Tag Gateway is a separate component
Google Tag Gateway (GTG) serves Google scripts and measurement requests through configured paths on first-party web infrastructure. Cloudflare provides a dedicated integration; a custom cookie Worker is neither a prerequisite nor automatically part of that integration.
GTG and an ssGTM container perform different jobs. A matching origin can allow connection reuse, but does not guarantee exactly the same IP across all requests or a particular latency. Forwarding and processing remain additional work. Google states that GTG processes Google first-party cookies and drops other forwarded first-party cookies. Enabling GTG alone therefore does not make a custom identifier available.
Handling an existing _ga cookie
Separate HTTP renewal of _ga requires verified coordination with the Google tag. The script must access this cookie, so HttpOnly is unsuitable for an ordinary script-read _ga configuration. Name, domain and path must match the existing cookie; otherwise multiple cookies can appear instead of a renewal.
cookie_domain: 'auto' and cookie_update affect Google tag behavior. cookie_update: false prevents ongoing tag-side renewal, but provides no ITP exception. Additional renewal must not override an earlier decision or deliberate deletion. Google’s documented default lifetime is also not a guaranteed browser lifetime.
Questions and answers
Does a cookie automatically break Cloudflare caching?
No. The documented default cache key does not include all cookie values. Custom cache keys and bypass rules can consider cookies, while Set-Cookie responses separately affect cacheability. Ignoring a cookie is appropriate only when content and access rights are demonstrably independent of that cookie.
Does a cache MISS establish a cookie problem?
No. Empty or expired caches, cache rules and response headers also matter. Repeated requests and the effective configuration need assessment together.
Does a Cookie request header reveal the expiry date?
No. It contains names and values, not the original attributes such as Max-Age. A renewal window needs a separately defined timing mechanism; an existence check is insufficient.
What needs testing before deployment?
Acceptance, rejection, withdrawal, subsequent requests, the first request without an identifier, parallel requests, cookie scope and cache isolation belong in browser and integration tests. Tests need the actual Safari and Chrome versions and the concrete GTG/ssGTM setup. No universal lifetime or compliance guarantee follows.
Sources
- Tracking Prevention in WebKit
- WebKit CNAME defense
- WebKit Private Browsing 2.0
- WebKit Tracking Prevention Policy
- Cloudflare default cache behavior
- Cloudflare cache keys
- Cloudflare Workers Cache API
- Cloudflare Google Tag Gateway
- Google Tag Gateway cookie handling
- Google first-party script serving
- Google cookie configuration
- MDN Set-Cookie
- Full Third-Party Cookie Blocking (WebKit)
- Cloudflare Workers documentation
- Google Analytics: Cookie usage on websites
- Chrome for Developers: Cookie Expires and Max-Age attributes now have upper limit
- RFC 6265: HTTP State Management Mechanism, Section 5.3
- Directive 2002/58/EC (ePrivacy Directive)