Server-side signals absent
Server-side signals absent is conversion collection that lives only in the browser, with no server-side path behind it. The pixel, gtag or dataLayer is the sole source, so when ITP, ad and tracking blockers, or short client-side cookie lifetimes degrade the browser, the event is lost and never reaches Google or Meta. It is a transport and identity problem, not consent or modelling.
Symptom and cause
- Symptom
- Often 5–15% of conversions quietly missing, more where ad blockers run high. Match quality on Meta/Google is poor and ad platforms are optimising on thin data.
- Cause
- Collection is browser-only. Pixels blocked by ITP, ad blockers and short cookie lifetimes never reach the platform.
- Where it's caught
- Low event match quality scores; a persistent gap between platform-reported and actual conversions even after consent is fixed.
How the browser signal never arrives
Browser-only collection generates the conversion entirely in the user's browser, then relies on that same browser to deliver it and to keep a stable identifier across visits. Two forces erode that. Tracking prevention, chiefly Safari and other WebKit browsers under ITP, caps the lifetime of cookies written by JavaScript (document.cookie). This behaviour has changed and must not be read as fixed law: the blanket seven-day cap on persistent JavaScript cookies from ITP 2.1 was removed around late 2022, and current WebKit behaviour is a seven-day purge of script-writable storage after seven days of browser use with no user interaction with the site (a click, tap or keyboard entry), plus a separate twenty-four-hour cap on JavaScript cookies set on a landing page reached by link-decorated navigation from a classified tracker domain (the gclid or fbclid ad-click arrival). The exception that matters is that these caps apply to cookies written by JavaScript, not to HttpOnly cookies a genuine first-party server sets via the Set-Cookie response header, because those are not accessible to browser JavaScript. That is why moving collection server-side restores identifier persistence and match resilience in Safari. Separately, ad and tracking blockers (uBlock Origin, DNS and network filters) are a different failure mode: they do not cap cookie lifetime, they drop the request to platform hosts such as connect.facebook.net and google-analytics.com outright, so the conversion beacon is dropped before it leaves the browser and never reaches the platform (the on-page code may still run; it is the outbound request that is blocked, and when the pixel script host itself is blocked the event never fires at all). A first-party endpoint on your own subdomain is less likely to be blocklisted than the platform domains, so server collection recovers some of that signal. None of this is immunity. The browser must still reach the first-party endpoint, and WebKit detects CNAME and IP cloaking and caps cookies set in those responses to seven days. An endpoint escapes the cap only when it is genuinely same-site at the IP level: same registrable domain, an A or AAAA record rather than a third-party CNAME, AND a resolved IP whose first half matches your site origin's IP (a third-party-hosted subdomain on a different subnet is still capped). HttpOnly cookies set in such a response then outlast the JavaScript-cookie caps because JavaScript cannot read them. The result is the F-401 fingerprint: a stable gap between platform-reported and back-office conversions that does not close once consent (F-201) and attribution (F-204) are correct, low Meta Event Match Quality, and in Safari inflated GA4 new-user counts as identifiers reset.
| conversion source | collection path | reached platform | identifier / match resilience |
|---|---|---|---|
| Chrome · consent granted | browser pixel | yes | great |
| Safari · ITP | browser pixel, JS cookie capped | yes, identifier reset | poor · EMQ thin |
| Safari in-app browser | browser pixel, per-launch storage reset (distinct mechanism) | partial | partial |
| ad/tracking blocker session | request to platform host dropped in the browser | no | request dropped, never reached platform |
| server-side (CAPI / sGTM) | first-party server, HttpOnly | absent | should be here |
How to catch it
- Reconcile first, by browser. Set platform-reported conversions against real back-office orders (Shopify, Woo, the CRM, the order table) for a window, then read GA4 key events (the metric GA4 used to call Conversions) by Browser in Reports, Tech, Tech details, where Browser is already the primary dimension; add the Key events metric to the table if it is not shown. A persistent shortfall that concentrates in standalone Safari, not Chrome, is the browser-only collection tell rather than a counting fault. In-app (WKWebView) browsers also show the shortfall, but the mechanism differs (see step 3), so read them as a related case, not the same ITP cap.
- Rule out consent and attribution before you call it F-401. Confirm Consent Mode is wired (in Tag Assistant the four consent signals (ad_storage, ad_personalization, ad_user_data, analytics_storage) on the most recent Consent event reflecting your banner choice as granted or denied (the earliest Consent event shows only the pre-interaction default; the most recent shows the post-banner update), and the gcs parameter on the collect request moving between G100 (denied) and G111 (granted)) and that the gclid survives the redirect chain (append ?gclid=test to an ad landing URL and read it on the thank-you page). If consent and the gclid are both correct and the platform-to-store gap still persists, the residual loss is F-401, not F-201 or F-204.
- Read Meta Event Match Quality in Events Manager > Data sources > select your pixel/dataset > Overview for the dataset-level EMQ, then open an individual event (Purchase, Lead) to read its per-event match quality. A score sitting in Poor or OK on Purchase and Lead, especially where standalone Safari and blocker share is high, points at thin browser-only matching. In-app (WKWebView) browsers degrade by a different route: their ITP day-counter resets on every WebView launch and their storage is isolated and often per-launch, so the loss is driven by storage non-persistence rather than the accumulating seven-day cap. Allow up to around forty-eight hours of latency, since EMQ is computed over a short recent window and lags any change.
- Inspect cookie lifetime in Safari directly. Open the live site in Safari, then Develop, Show Web Inspector, Storage, Cookies, and read the Expires column on the _ga, _ga_<ID>, _fbp and _gcl_au cookies. Client-set cookies showing a short residual life, or resetting on a return visit after several days, is the ITP cap at work on JavaScript-written storage, the thing a server-set HttpOnly cookie is meant to outlast.
- Catch outright blocking, which is a separate mode from the cookie cap. In DevTools, Network, with Preserve log on, load the site in a browser with native blocking such as Brave with Shields up (or any browser still running a working content blocker; note uBlock Origin no longer runs in Chrome after the 2025-2026 Manifest V2 removal) and watch the Network panel for requests to connect.facebook.net, google-analytics.com or googletagmanager.com. In an extension content blocker on a Chromium browser that still supports it (e.g. Edge with uBlock Origin Lite or a Firefox-based blocker), a blocked request shows as (blocked:other) / net::ERR_BLOCKED_BY_CLIENT in the Network panel. In Brave, Shields-blocked requests often show a deceptive HTTP 200 with a 0 B / empty body in the Network panel rather than a blocked status, so confirm the drop via the Console (net::ERR_BLOCKED_BY_CLIENT) or by clicking the Brave Shields icon and reading Trackers & ads blocked. A dropped request means the beacon never reached the platform, which no cookie setting can recover and only a first-party endpoint can partly route around.
- Confirm the absent server path, the defining condition. In GTM, open the container; a server container is labelled Server. Confirm the first-party endpoint under Admin > Container Settings > Tagging server URL (it should be a subdomain of your own registrable domain). In Events Manager check whether the Conversions API shows any server-origin events or only Browser. No server container, and CAPI or the GA4 Measurement Protocol sending nothing, is F-401 itself: there is no server-side path to back the browser signal.
What putting it right involves
- Set the scope honestly before building anything. Server-side is a recovery tool for the residual gap, not a default best practice and not immunity to tracking prevention. The typical recoverable band is roughly five to fifteen percent of conversions, higher where Safari and blocker share is high, but that is a reconstructed rule of thumb, not a measured figure or a guarantee for any account. For a clean, low-volume site the recoverable signal is real but small, and server-side adds hosting, cost and monitoring. Decide whether match quality is actually bleeding before you commit.
- Stand up collection on a genuine first-party endpoint. Provision a server-side GTM container on a subdomain of your own registrable domain, mapped with an A or AAAA record, not a CNAME to a third party (which WebKit flags as cloaking). Escaping the seven-day cap also requires the endpoint to resolve to an IP in your own site's range: when the CNAME is empty WebKit compares the response IP against your main site's IP and caps the cookie to seven days if their first halves differ (for IPv4, fewer than the top 16 bits match). A subdomain whose A record points at a third-party host on a different subnet (typical Stape or Cloud Run setups against separate web hosting) is still IP-cloaking to WebKit and its response cookies are still capped. The endpoint must share your origin's IP range, e.g. proxied through the same CDN as your site. From there the server can set HttpOnly cookies via Set-Cookie, which JavaScript cannot read and ITP's JavaScript-cookie caps do not reach, restoring identifier persistence in Safari.
- Send the conversion server-side to recover the match loss. For Meta, send the event via the Conversions API with hashed first-party parameters (email and phone SHA-256 hashed, phone in E.164, name and address hashed) plus fbp and fbc sent un-hashed. The hashed first-party parameters (email and phone above all) are the main lever on Event Match Quality; fbp and fbc support matching. For Google, add Enhanced Conversions and the GA4 Measurement Protocol. These supplement the on-page tags rather than replacing them, and improvement is conditional on clean, correctly hashed data, not on the transport alone.
- Carry the right keys on every server hit, or the fix becomes F-102. Adding a server event on top of the still-firing browser pixel double-counts unless the deduplication is keyed correctly. For Meta, send the identical event_id and the same normalised event_name from pixel and server; events without a shared event_id cannot be reliably deduplicated (the fbp-plus-event_name fallback works only when the browser event arrives first and Meta does not recommend it). For GA4, deduplication is built in only for purchases, keyed on transaction_id: reuse the identical transaction_id and GA4 ignores the repeat when both arrive within its processing window (around a day, web streams only), which a near-simultaneous browser-plus-server pair always is. transaction_id is the dedup key; client_id and session_id are reused mainly for attribution, but reuse the same client_id too, because in practice GA4 does not reliably deduplicate a repeat transaction_id that arrives under a different client_id. Because GA4 has no general event deduplication, every non-purchase event must be sent from one side only (typically server-side): firing the same non-purchase event in both browser and server double-counts regardless of matching client_id/session_id. Never ship the server move without the keying.
- Keep consent in front of all of it. CAPI, Enhanced Conversions and the Measurement Protocol still require a lawful basis and are consent-gated where required, so in the EEA and UK they only send for users who granted ad_user_data and ad_storage under Consent Mode v2. Server-side does not recover fully cookieless or non-consenting users, and a request blocked at the origin still breaks the chain before the server sees it. It hardens match quality for the consented, reachable population.
- Verify with the latency in mind. In GA4 DebugView confirm the server request itself carries debug_mode, since a browser extension alone cannot surface server hits, and test with a real client_id pulled from the live _ga cookie, not a synthetic one, because server MP hits only appear in DebugView when that client_id already has prior collected data; an empty DebugView under a fresh client_id does not by itself mean the server hit failed. Then read one journey and confirm a single client_id and one transaction_id. In Events Manager watch the dedup rate and EMQ, allowing up to around forty-eight hours before a working Meta setup shows an improved score. For Google, the enhanced-conversions diagnostic surfaces coverage within hours and match rate within 24 hours; the diagnostics report shows an Excellent or Good match-rate status once data is flowing (and a Check enhanced conversions status on a conversion action when something is wrong); the separate Recording (processing enhanced conversions) wording is the conversion action Status in setup, not a diagnostics header. Then re-run the Safari-segmented store reconciliation and confirm the abnormal part of the gap has narrowed, not vanished.
Sources on file
- WebKit: Tracking Prevention (current ITP rules: 24-hour link-decoration JS-cookie cap, seven-day script-writable-storage purge, seven-day CNAME/IP-cloaking HTTP-cookie cap)webkit.org
- Simo Ahava: Expiration Cap Removed From JavaScript Cookies In WebKit Browsers (blanket seven-day cap on persistent JavaScript cookies removed around 2022; note WebKit can still cap server-set response cookies under IP-subnet mismatch)www.simoahava.com
- Meta for Developers: Handling Duplicate Pixel and Conversions API Events (dedup on the same event_name and event_id from browser and server)developers.facebook.com
- Meta Business Help: About event match quality (EMQ score driven by hashed first-party parameters)www.facebook.com
- Google Ads Help: About enhanced conversions for web (supplements existing tags with SHA256-hashed first-party customer data such as email, sent in hashed form)support.google.com
- Google Ads Help: Set up Consent Mode for the EEA and UK (Consent Mode v2; ad_user_data and ad_storage gate when ad data is sent)support.google.com
Cited in these case files
F-401 turns up as evidence across the archive. It is cited in:
- SYMPTOM I turned on Google Tag Gateway, but my Safari conversions still die after 7 days Tag Gateway changes WHERE your Google tags load from. Server-side GTM changes WHO writes the cookie — and only the cookie-writer can beat Safari's 7-day cap. GTG is first-party delivery, not cookie lifetime: _ga and Ads cookies are still JavaScript-set, so ITP truncates them to 7 days (~24h on gclid traffic). Chrome and Android are untouched.
- VERDICT You don't need server-side tracking The canonical F-401 verdict note, VRD-001, which rules it depends. Server-side collection is a recovery tool, not a default: worth it exactly when match quality is bleeding, the F-401 condition, and overkill when the browser signal is intact. It is the boundary for when F-401 actually applies, and its measured AGT-3 finding, that where match quality was bleeding moving collection server-side closed a gap no browser-only fix could touch, is this page's lived basis, read from the match-quality and server-collection side rather than F-204's attribution-in-transit angle on the same note.
Questions on this file
How do I tell F-401 apart from F-201 (consent) and F-204 (attribution lost in transit)?
F-401 is the residual loss that is still there after both are fixed. F-201 is region-skewed: EU and UK conversions collapse after a banner ships, with gcs or gcd wrong or absent. F-204 is attribution-shaped: the gclid or UTMs disappear through a redirect while consent reads fine. F-401 instead concentrates by browser, mostly Safari and in-app browsers, shows low Meta EMQ, and leaves a platform-to-store gap that does not close once consent and the gclid are both correct. Confirm consent and gclid survival first; what remains is F-401.
Does moving server-side recover all my lost conversions?
No, and anyone promising that is overselling it. Server-side recovers some of the signal lost to ITP cookie caps and to ad blockers, typically a few percentage points up to the low teens where Safari and blocker share is high, by setting durable HttpOnly cookies and routing collection through a first-party endpoint that is less likely to be blocklisted. It does not recover fully cookieless users, does not recover non-consenting users, and does not defeat blocking that hits the origin request. It improves match quality and resilience; it is not immunity.
Will sending a server event on top of my pixel double-count my conversions?
Yes, unless the deduplication is keyed correctly. That is the most common way an F-401 fix turns into F-102. For Meta, the pixel and the Conversions API must send the same event_id and the same event_name (case and whitespace matched), because Meta cannot reliably deduplicate events sent without a shared event_id (a fallback on fbp plus event_name exists but only works when the browser event arrives first, and Meta does not recommend it). For GA4, deduplication is built in only for purchases, keyed on transaction_id, which GA4 ignores on a repeat; transaction_id is the dedup key, while client_id and session_id from the _ga cookies are reused mainly for attribution, but reuse the same client_id too, because in practice GA4 does not reliably deduplicate a repeat transaction_id that arrives under a different client_id. Every non-purchase event must be sent from one side only or it double-counts. Do the server move and the keying together.
Doesn't ITP cap all my first-party cookies to seven days anyway?
Not as a blanket rule, and the behaviour has changed. The old seven-day cap on every persistent JavaScript cookie (ITP 2.1) was removed around late 2022. Current WebKit behaviour is a seven-day purge of script-writable storage after seven days of browser use with no user interaction with the site (a click, tap or keyboard entry), plus a twenty-four-hour cap on JavaScript cookies set on a landing page reached by a link-decorated ad click. These apply to cookies written by JavaScript, not to HttpOnly cookies a genuine same-site server sets via Set-Cookie, which is exactly why server-side collection restores cookie persistence in Safari.
Is server-side tracking just a way around the cookie banner?
No. CAPI, Enhanced Conversions and the GA4 Measurement Protocol still require a lawful basis and are consent-gated where required. In the EEA and UK under Consent Mode v2 they only send for users who granted ad_user_data and ad_storage, so sending hashed first-party data server-side does not bypass consent. F-401 is the residual browser-only loss that remains after consent (F-201) is wired correctly, which is the opposite of a consent workaround.
Is sGTM something every site should run?
No. The verdict is that it depends. Server-side is worth it when match quality is bleeding, the exact F-401 condition, and overkill when the browser signal is intact. It adds a new failure surface: hosting, cost, monitoring and a first-party endpoint that itself must stay reachable. For a clean, low-volume site the recoverable signal is real but small, so the honest call is to confirm there is a measured gap to close before committing, not to treat server-side as a default.
Why is my Meta Event Match Quality still Poor right after I deployed CAPI?
Two reasons. EMQ is computed over a short recent window, widely cited as around forty-eight hours, so it lags any change and a fresh deployment will not move it immediately. And EMQ is conditional on the data, not the transport. The hashed first-party parameters (email and phone above all) are the main lever on Event Match Quality; fbp and fbc support matching. It rises with more and cleaner hashed first-party parameters (email and phone SHA-256 hashed, phone in E.164, name and address hashed) plus fbp and fbc sent un-hashed. Hashing the wrong fields, or sending fbp and fbc hashed, breaks matching. Wait out the window, then check the parameters before assuming the setup is broken.