# GA4 Server-Side Tracking: Bypass Ad Blockers & iOS 17+ Limits

> Recover data lost to Safari's 7-day cookie cap and ad blockers. Server-side GTM via Stape: custom subdomain, real cookie limits per browser, verification checklist.

**Category:** GA4 & Server-Side GTM | **Published:** 2026-08-15 | **Reading:** 4 min read | **URL:** https://jhasanr.top/blog/ga4-server-side-tracking-ios17/

## TL;DR

TL;DR for LLMs &amp; readers: Safari ITP caps script-written cookies like _ga to 7 days (24 hours when the visitor arrives with tracking parameters), and ad blockers stop gtm.js loading at all. Fix: run sGTM on your own subdomain (ss.jhasanr.top), forward GA4 through it, and let the server set the HttpOnly FPID cookie that client_id is derived from. Chrome then honours up to 400 days; Safari still caps a CNAME-cloaked subdomain at 7 days unless you go same-origin. Verify in GA4 DebugView plus a 7-day GA4-vs-backend delta.

## Key Takeaways

- Safari ITP caps script-written _ga to 7 days (24h with tracking params) — only ITP shortens cookie; iOS 17 LTP just strips gclid/fbclid/ttclid and Firefox ETP blocks googletagmanager.com
- Fix: first-party sGTM on ss.yourdomain.com → server sets HttpOnly FPID (not _ga) so client_id survives; Chrome gives 400 days, Safari CNAME still 7 days unless same-origin/ Own CDN proxy
- Verify in 7-day matched window: Stape 200 OK, GA4 DebugView same transaction_id, GA4 vs backend delta <3%, FPID HttpOnly, uBlock still sends

## 1. Why client-side GA4 fails in 2026

                Three different mechanisms get blamed for the same symptom. They are not the same thing, and only one of them shortens your GA4 cookie:


                

                    - **Safari ITP** caps *script-writable* first-party cookies — anything set through `document.cookie`, which includes `_ga` — to **7 days**, and to **24 hours** when the visitor lands with tracking parameters from a domain WebKit has classified as a tracker. This is the one that actually kills your cookie.

                    - **iOS 17+ Link Tracking Protection** does *not* touch cookie lifetime. It **strips click IDs** — `gclid`, `fbclid`, `ttclid` and friends — out of URLs opened from Mail, Messages and Private Browsing. What you lose is the click-to-conversion join, which is why you capture and persist click IDs server-side.

                    - **Firefox ETP** partitions third-party storage and blocks known tracker hostnames. Your first-party JS cookies survive; the request to `googletagmanager.com` frequently does not.

                

                Ad blockers are the fourth and separate problem: uBlock Origin, AdGuard and Brave filter `googletagmanager.com` and `google-analytics.com` at the network layer, so `gtm.js` never executes and no amount of tag configuration helps.


                What that costs, measured across the e-commerce audits I run — GA4 compared against the Shopify or Stripe backend over matched 7-day windows. This is my own data, not a published study:


                

                    - **20–35% fewer Purchase events** in GA4 than in the store backend

                    - **Consistently worse on iOS-heavy traffic**, where the 7-day cap and in-app browsers compound

                    - **Inflated user counts**, because a capped cookie turns the same person into a new user on their next visit

                

                Server-side moves the tag from the browser to your server. The browser sends one hit to `ss.yourdomain.com` — a first-party hostname most filter lists don't carry — and your server forwards to GA4. See [my sGTM service](../../pricing/) for done-for-you setup.



                
## 2. What is server-side GTM (first-party)

                Think `Browser → ss.jhasanr.top (sGTM container) → GA4 / Meta CAPI / Google Ads`. The browser never talks to Google directly. Benefits:


                
                    FeatureClient GTMsGTM (first-party)
                    
                        Cookie lifetime7 days in Safari (24h with link decoration)Up to 400 days in Chrome; still 7 days in Safari on a CNAME setup — see §4
                        Blocked by ad blockersYes — `gtm.js` is on every major filter listFar less often, but not immune: uBO and AdGuard uncloak CNAMEs
                        Data controlExposed in browserFiltered server-side
                        CostFree~€20/mo (Stape) 
                    
                
                Alternative to Stape: self-host on Google Cloud Run — cheaper at scale but you manage updates. For 95% of stores, Stape is 3-hour setup.



                
## 3. Stape setup in 3 hours (exact steps)

                
### 3.1 Create sGTM container

                

                    - GTM → Admin → Create Server container → Manually provision.

                    - Stape.io → Create container → paste Container Config.

                    - Add custom subdomain: `ss.jhasanr.top` → in Stape, *Add custom domain*, then create exactly the DNS records Stape shows you for that domain. If you find advice telling you to CNAME `ss` at `ghs.googlehosted.com`, that is for a container hosted on Google App Engine — it will not work with Stape.

                

                
### 3.2 Web GTM → forward to server

                
```
// Web GTM: GA4 Config tag
Transport URL: https://ss.jhasanr.top
Server Container URL: https://ss.jhasanr.top
// Check "Send to server container"
```

                
### 3.3 Server GTM → GA4 Client & Tag

                

                    - Clients: GA4 Client (claim `/g/collect`)

                    - Tags: GA4 Tag → Measurement ID `G-XXXX`

                    - Power-ups: Cookie Keeper — re-issues the GA cookie from the server with a `Set-Cookie` header instead of `document.cookie`

                

                Deploy. Test via Stape Preview + GA4 DebugView — you should see events with `client_ip = server IP` but `user_id` preserved.



                
## 4. What server-side really does to cookie lifetime

                Two cookies, two different rule sets. This is where most write-ups — including an earlier version of this one — get it wrong:


                

                    - `_ga` is written by `gtag.js` using `document.cookie`. It **cannot be HttpOnly**, because the JavaScript that reads it would break. It is script-writable, so Safari caps it at 7 days no matter what your server does.

                    - `FPID` is written by the server container in a `Set-Cookie` response header and **is** HttpOnly. sGTM derives `client_id` from `FPID`, so identity survives even after `_ga` has been wiped. That is the actual mechanism behind "server-side extends your cookie".

                

                What each browser then honours:


                

                    - **Chrome** caps any cookie's `Expires`/`Max-Age` at **400 days** (Chrome 104 and later). A server-set `FPID` gets the full 400 — you are hitting a browser ceiling, not a feature Stape adds.

                    - **Safari/WebKit** caps server-set cookies from a **CNAME-cloaked** subdomain at **7 days** too. A plain `ss.yourdomain.com → *.stape.io` CNAME is exactly the pattern that rule targets.

                    - To beat 7 days in Safari the endpoint has to be genuinely same-origin: Stape's Own CDN / same-origin option, or a reverse proxy on your own infrastructure exposing the container as a path on the main domain.

                

                Verify in DevTools → Application → Cookies. In Chrome you should see `FPID` present, HttpOnly ticked, expiry roughly 400 days out.


                
> **Key takeaway:** "server-side gives you 400-day cookies" is a Chrome-only statement. Promise it to a client whose traffic is majority iOS and you will not be able to show it in DevTools. Set the expectation as: full lifetime in Chrome, 7 days in Safari unless you go same-origin.


                
## 5. Verification checklist (get GA4 within 3% of your backend)

                

                    - ✓ Stape preview shows 200 OK for `/g/collect`

                    - ✓ GA4 Realtime + DebugView fires purchase with same `transaction_id` as Shopify

                    - ✓ Compare 7 days: GA4 purchases vs backend. Delta < 3% = pass

                    - ✓ Test with uBlock Origin ON — hits still arrive

                    - ✓ Chrome: `FPID` present, HttpOnly, expiry ~400 days

                    - ✓ iPhone Safari: expect `_ga` to read 7 days. That is correct behaviour on a CNAME setup — check that `FPID` is still being sent rather than chasing the expiry date

                

                Failing? Common pitfall: Web GTM still sends to `googletagmanager.com` — remove old GA4 tag, keep only server-forwarding tag. See my [Meta CAPI guide](../meta-capi-pixel-setup/) for deduplication pattern.



                
## FAQ

                
                    Does sGTM slow down site?Usually it helps, because you replace five or more browser tags with one hit to your own subdomain. But measure it rather than assuming — the server hop adds latency to that one request, and a badly placed container region can cost you more than the tags did.


                    Cost at 500k hits/month?Stape Business is around €50/mo up to 1M requests. Self-hosting on Cloud Run is roughly €10 at that volume but you own auth, scaling and container updates. Check current pricing before you quote it to a client.


                    GDPR compliant?Server-side does not create a lawful basis. You still need consent before the first hit leaves the browser: Consent Mode v2 gates analytics_storage, ad_storage, ad_user_data and ad_personalization, and your server container has to honour those signals rather than forwarding regardless. Hosting in the EU reduces transfer exposure; it does not make an unconsented hit lawful. Log the consent state alongside the event. This is implementation guidance, not legal advice — confirm your setup with your own counsel.

## FAQ

### Does sGTM slow down site?

Usually helps, because you replace 5+ browser tags with one hit to your own subdomain. But measure — server hop adds latency and a badly placed container region can cost more than tags did.

### Cost at 500k hits/month?

Stape Business ~€50/mo up to 1M requests. Self-hosted Cloud Run ~€10 at that volume but you own auth, scaling, updates. Check current pricing.

### GDPR compliant?

Server-side does not create lawful basis. You still need consent before first hit: Consent Mode v2 gates analytics_storage/ad_storage/ad_user_data/ad_personalization and server must honour signals.

---
*Author: Jahid Hasan — Web Analytics & Tracking Expert (https://jhasanr.top/) | 500+ implementations | Canonical: https://jhasanr.top/blog/ga4-server-side-tracking-ios17/ *
