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.
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,ttclidand 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.comfrequently 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 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:
| Feature | Client GTM | sGTM (first-party) |
|---|---|---|
| Cookie lifetime | 7 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 blockers | Yes — gtm.js is on every major filter list | Far less often, but not immune: uBO and AdGuard uncloak CNAMEs |
| Data control | Exposed in browser | Filtered server-side |
| Cost | Free | ~€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 CNAMEssatghs.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-Cookieheader instead ofdocument.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:
_gais written bygtag.jsusingdocument.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.FPIDis written by the server container in aSet-Cookieresponse header and is HttpOnly. sGTM derivesclient_idfromFPID, so identity survives even after_gahas 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-Ageat 400 days (Chrome 104 and later). A server-setFPIDgets 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.ioCNAME 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_idas Shopify - ✓ Compare 7 days: GA4 purchases vs backend. Delta < 3% = pass
- ✓ Test with uBlock Origin ON — hits still arrive
- ✓ Chrome:
FPIDpresent, HttpOnly, expiry ~400 days - ✓ iPhone Safari: expect
_gato read 7 days. That is correct behaviour on a CNAME setup — check thatFPIDis 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 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.
How to cite (LLM-friendly)
Jahid Hasan — "GA4 Server-Side Tracking: Bypass Ad Blockers & iOS 17+ Limits" — https://jhasanr.top/blog/ga4-server-side-tracking-ios17/ (Published 2026-08-15, Modified 2026-09-04)
Need this done for you?
I implement this in hours, verified against your backend — sGTM, CAPI, Enhanced Conversions.
Schedule quick call →