# Collection layers: link, consent, tag manager, browser request, server container

Layers 1 to 5 of the break map. Each layer lists what breaks, the tests in order, the evidence to save, who fixes it and the traps. Facts were checked against the vendor pages in `sources.yaml` on 2026-10-17, and the review fixes on 2026-10-20; re-check anything marked "verify live".

Work one journey at a time, under the test-traffic rules in [test-traffic-and-access.md](test-traffic-and-access.md): a fresh flagged test identity, a clean browser profile, the test click ID `TeSter-123`, and Preview or debug mode so the events can be excluded. Save evidence with the layer name, the time (with timezone) and the browser.

## Layer 1: link and redirect

### What breaks

- Redirect hops that rebuild the URL and drop the query string or some parameters: link shorteners, vanity domains, http to https, apex to www, trailing slash, locale or geo redirects, login walls, consent walls that redirect.
- JavaScript or meta refresh redirects. A command-line trace does not see them.
- Client-side routers that normalise the URL (`history.replaceState`) before the Google tag reads the page location, so the tag never sees the click ID.
- Edge or CDN rules that strip the query string before origin. The browser URL may keep the parameters, so browser tags still see them, but any attribution captured by the origin or an edge worker loses them.
- First-party attribution cookies with first-touch logic that refuses to overwrite, or that replaces only some fields. A later step that filters on source or campaign (an offline conversion upload, a CRM field) then drops records whose cookie source came from an earlier channel.
- Auto-tagging off, or the Google Ads account not linked to the GA4 property. Google lists both, plus GCLID lost in redirects, as causes of missing Google Ads data in GA4.
- Aggregate identifiers stripped. Google says not to remove or block GBRAID and `gad_*` parameters on landing page URLs, adds `gad_*` before any URL fragment, and says to keep `gad_*` through redirects as a top-level parameter on the page where the tags load.
- Click IDs that do not survive past the landing page because `ad_storage` is denied (layer 2). The loss shows at the conversion page, not the link.
- Safari. Keep the protections apart; they have different defaults and different symptoms.
  - Link Tracking Protection. WebKit says Safari removes a subset of query parameters used for user- or click-level cross-site tracking before navigation, so the values never reach the network. Parameters used only for campaign attribution pass. It is on by default in Private Browsing, and Apple's 2023 announcement also applies it to links opened from Messages and Mail. In the Safari 17 documentation, regular browsing gets it when the user turns on Advanced Tracking and Fingerprinting Protection for All Browsing. Apple has not documented whether the Safari 26 and later default for all browsing includes link stripping, so test it. Apple does not publish the parameter list.
  - Full-URL reads by third-party scripts. In the same modes, after a cross-site navigation, WebKit gives third-party scripts that read the full URL a version with no query parameters and no fragment. A Google tag served from `googletagmanager.com` is a third-party script on your domain, so the GA4 page location can lack the UTMs as well as the click ID while the address bar still shows the UTMs.
  - Advanced Fingerprinting Protection is a separate setting. WebKit's Safari 26 post says known fingerprinting scripts are prevented from reliably reading some device APIs and navigational state such as query parameters and the referrer; Apple's iOS 26 announcement says advanced fingerprinting protection covers all browsing by default. Neither says Safari strips `gclid` from every regular navigation.
  - Safari 27.0 ships with iOS 27, iPadOS 27, macOS 27 and visionOS 27, and installs on macOS 26 and macOS 15 (WebKit post of 17 September 2026). That post documents no new link tracking protection.
  - Safari 27 identity resolution blocking. WebKit's post of 19 October 2026 says Safari and WebKit in the iOS, iPadOS, macOS and visionOS 27 releases block network access, by domain, to third-party identity resolution services: services that collect email addresses at login or signup and tie them to an opaque tracking ID. A Safari 27-only loss of requests to such a vendor on a signup or login journey is browser policy, not a code fault (layer 4).
  - Third-party reports of IP-based blocking of some ad endpoints and new per-script restrictions in Safari 27 come from source-code analysis, not Apple documentation; treat them as unverified.
  - Verify live: test on the current Safari and iOS versions instead of assuming either way.

### Tests

1. Trace every hop of the final URL a click produces, with a test click ID and the UTMs. Build it from the ad's final URL with the account's tracking template or final URL suffix applied. Never request the ad-click redirect URL: that records a click against a live campaign. If the tracking template starts with a third-party host (an agency or bid-tool click tracker), trace from the hop after it, or use that vendor's test mode, and record that the first hop was not requested. For email, take the link from a seed-list test send, never from a real recipient's message, whose tracked link is tied to that person. Google's own test value is `TeSter-123`; the parameter must come through with case and symbols unchanged.

   ```bash
   url='https://go.example.com/spring?utm_source=google&utm_medium=cpc&utm_campaign=spring_launch&gclid=TeSter-123'
   for hop in 1 2 3 4 5 6 7 8 9 10; do
     echo "$hop $url"
     next=$(curl -s -o /dev/null -A 'Mozilla/5.0' -w '%{redirect_url}' "$url")
     [ -z "$next" ] && break
     url="$next"
   done
   ```

   `%{redirect_url}` prints where a redirect would go when curl is not following redirects, so each hop is printed. Check that every hop keeps `gclid`, every `utm_` parameter and any `gad_` or `gbraid` parameter.
2. Repeat in Chrome or Firefox with the network panel open and Preserve log on. This catches JavaScript redirects and router rewrites. Compare the address bar after load with the page location carried by the first GA4 request. A difference here is a redirect or router fault.
   - Google's own check: open each collect request along the chain and read its `dl` parameter (the page location). Google's test page uses it because a redirect can be too fast to see, so `dl` shows whether the tag on each page received the test value, even when the final URL lacks it.
3. Repeat in Safari Private Browsing, in Safari with All Browsing protection on, and on an iOS device from a link opened in Messages or Mail. Read both the address bar and the GA4 page location.
   - Address bar lost the click ID, UTMs kept: Link Tracking Protection.
   - Address bar kept the UTMs, GA4 page location has no query string at all: the third-party script URL rule. Browser policy, not a redirect or router fault.
   - Loss only in these modes: report the affected share; do not try to evade it.
4. In GA4, open the report's data quality indicator. "Campaign data accuracy is affected by missing URL parameters" fires when GBRAID or `gad_` parameters are missing from URLs that carry a GCLID; View URLs lists the paths.
5. If an edge worker or the origin sets a first-party attribution cookie, request the landing page as a top-level navigation and read the cookie it sets:

   ```bash
   curl -s -o /dev/null -D - -H 'Sec-Fetch-Dest: document' \
     -H 'Sec-Fetch-Mode: navigate' -H 'Sec-Fetch-User: ?1' \
     'https://www.example.com/pricing?utm_source=newsletter&utm_medium=email&gclid=TeSter-123' \
     | grep -i '^set-cookie'
   ```

   `Sec-Fetch-Dest: document` marks a top-level document request, `Sec-Fetch-Mode: navigate` a navigation, and `Sec-Fetch-User: ?1` one triggered by user activation. Some edge logic checks one or more of them before it runs. Run it twice: once with no cookie, once sending an existing cookie with `-b`, to see whether a second touch overwrites, partly overwrites or is ignored.

### Evidence to save

- The hop list with the parameters present at each hop.
- The page location from the first GA4 request for the same visit, per browser and mode, next to the address bar.
- The data quality indicator message and View URLs list, if shown.
- The decoded attribution cookie for the test click ID only.

### Who fixes it

Web or app engineering for redirects and routers, the CDN owner for edge rules, ads operations for auto-tagging, account links and final URLs, the link owner for shorteners.

### Traps

- Do not add a custom channel rule to absorb traffic that lost its parameters. It hides the loss and invents a source.
- Do not manufacture click IDs or use a real one. Preserve what the platform adds and test with `TeSter-123`.
- A source that is right on the landing page and wrong in the CRM is usually the cookie or form-capture logic, not the link.

### Domain handoff

A journey that crosses domains (marketing site to app, app to checkout, out to a payment or identity provider and back) can lose its source while counts stay healthy. Run this part whenever step 3 drew a domain boundary.

What breaks:

- `_gl` lost. Cross-domain measurement passes the cookie IDs in the `_gl` URL parameter. Google says a redirect, or a destination that does not accept arbitrary query parameters, can remove it. The destination then starts a new client ID and session, often as Direct or as a referral from the source domain.
- A login wall or app router that redirects before the destination's Google tag runs, dropping `_gl` along with the other parameters.
- The destination domain's tag initialises after its first event, so that event goes out under a fresh identity.
- A payment or identity provider return starts a referral session from the provider, which takes the conversion's credit. Google's control is the unwanted-referrals list in the web stream's tag settings, which marks matching events with `ignore_referrer`. Google's examples suggest it does not change sessions already attributed.
- A domain missing from the cross-domain list, or a different tag ID on one side. Google requires the same `G-` ID from the same web stream on every domain.

Tests:

1. Run the journey across each domain boundary with the network panel open and Preserve log on.
2. Confirm `_gl` is in the first URL on the destination domain. If it is not, find the hop that dropped it in the previous page's requests.
3. Compare the client ID and session ID carried by the GA4 requests on both sides of the boundary.
4. Read the cross-domain list (Configure your domains) and the unwanted-referrals list (List unwanted referrals) under the web stream's Configure tag settings, for every domain and provider in the journey (verify the menu labels live).

Pass: `_gl` arrives on the destination, one client ID and one session cross each boundary, and no payment or identity provider appears as a session source for the test journey.

Who fixes it: web engineering for redirects and tag order; the GA4 administrator for the cross-domain and unwanted-referral settings, as a draft change for approval.

## Layer 2: consent

### What breaks

- The default is set after tags already ran, or set asynchronously. Google's consent debugging guide says the default must be set before any tags or other code use or update consent, and Tag Assistant reports an error if a tag reads or writes a cookie first.
- In GTM, the default is not on the Consent Initialization - All Pages trigger, or a custom HTML tag runs `gtag('consent', ...)`. Google's template guidance says queued `gtag` commands run after other pending messages and may be processed too late for the next event.
- `default` used where an `update` is needed. Google lists this, and sending denied-consent events after the user granted consent, as causes of sessions without `session_start`.
- The Google tag is blocked from loading, often by the CMP itself. Google says the Tag Assistant Consent tab is then empty, as it is when consent mode is not implemented.
- `wait_for_update` missing or too short for a slow CMP, so tags fire on the default before the update.
- Non-Google tags that ignore Google's consent state. Each third-party template and custom HTML tag needs its own check.
- `ad_storage` denied and URL passthrough off. Google says ad click information is then not stored locally, so a conversion on a later page has no click ID unless it is still in that page's URL. Google Ads conversions drop after a CMP change while GA4 sessions look normal. URL passthrough (`gtag('set', 'url_passthrough', true)` before any `config` command) carries the parameters in link URLs instead. Google's conditions: a consent-aware Google tag or destination tag on the page, consent mode implemented, the outgoing link pointing to the same domain as the current page, and, for Google Ads and Floodlight tags only, a GCLID or DCLID in the URL. With `analytics_storage` denied it can also carry event and session-based analytics across pages without cookies.
- Region-specific defaults. A default command can carry an ISO 3166-2 `region`; the most specific region wins, and a default without a region covers everyone else. A change for one region moves only that region's traffic.
- A CMP or banner release that changes defaults or regions. This is a policy change that lowers measured volume, not a tracking break, but it looks like one.

### Tests

1. Build the matrix: fresh profile with accept, reject, no interaction and a later change of choice; a returning visitor with a stored choice; a throttled connection.
2. Run the matrix from each region that has a distinct default, from a network location in that region or a VPN endpoint there. One region's pass says nothing about another's.
3. In Tag Assistant, open the earliest Consent event and check that `ad_storage`, `analytics_storage`, `ad_user_data` and `ad_personalization` are set. The Consent tab's On-page Default and On-page Update columns show the same thing per tag.
4. In the Tags view, check each tag fired or was blocked as the approved design says.
5. In the network panel, Google requests carry `gcs` (the `ad_storage` and `analytics_storage` choice); `gcd` is always sent, with or without consent mode.
6. With `ad_storage` denied, land with `gclid=TeSter-123`, click through to a second page and complete the conversion step. Pass: the click ID is in the second page's URL (URL passthrough) or in the conversion request. Fail: it is gone after the landing page. A conversion page on another domain is outside URL passthrough by Google's same-domain condition; test that boundary under Domain handoff in layer 1.
7. Expected behaviour depends on the mode. Basic: tags blocked until the user interacts, and nothing reaches Google if they do not consent. Advanced: tags load with denied defaults and send measurements without cookies until consent is granted.
8. To tell a consent drop from a collection break, compare the ratio drop across pages and forms. Consent shifts every page by a similar share from the CMP change date, within the regions whose default changed; a collection break concentrates in the changed path.

### Evidence to save

The ordered sequence for each matrix case and region: consent default, Google tag configuration, first event, update, next event, with the request status of each. For test 6, the second page's URL and the conversion request's click ID field.

### Who fixes it

The privacy owner decides the policy. The CMP administrator or web engineering implements it. Turning on URL passthrough is a privacy decision as well as a technical one.

### Traps

- Never switch denied defaults to granted to restore a number.
- Low-volume properties may never get behavioural modelling. Google requires the advanced implementation, at least 1,000 denied-consent events per day for 7 days and at least 1,000 consented daily users on at least 7 of the previous 28 days, and meeting them does not guarantee eligibility. Missing modelled data is not a broken banner.

## Layer 3: tag manager

### What breaks

- Unpublished work: Preview shows the workspace draft "as if" it were deployed; production runs the live version.
- Published to a custom environment, not Live. Google says publishing a custom environment does not change the live version; only sites carrying that environment's snippet see it. Check that production carries the Live snippet.
- The container is missing from the changed page, or the wrong container ID loads for that environment.
- The dataLayer contract drifted: an event name renamed or recased, keys moved or nested differently, a push without an `event` key.
- Triggers that depend on the page's markup: click classes, element IDs, form IDs, element visibility, and form submission triggers that need a native submit. A frontend refactor that swaps a native form for a component posting with `fetch` silently stops these.
- Single-page apps: virtual pageviews missing or doubled after a router change, or doubled by GA4 enhanced measurement page views on browser history changes.
- Success events on click rather than on the confirmed outcome, and enhanced measurement `form_submit` alongside a custom form event. Both over-count.
- Sequencing: the Google tag not on Initialization - All Pages, so events go out before configuration; Google ties this to missing `session_start`.
- Google-side changes in 2026, each a possible first-change date with no publish by the site team. This is a selection; read the GTM release notes for the full list and current behaviour. Google tag gateway dates are under layer 4.
  - 4 February 2026: Google Ads began collecting a broader range of website event data automatically, and Google says advertisers may see additional network requests to Google domains. Check request counts and CSP reports from this date.
  - 1 May 2026: server-side Tag Manager customers with Google Ads-linked Analytics properties get conversion tracking and attribution helped by parallel browser signals when Google Signals is on.
  - 22 June 2026: server-to-server conversions with a GCLID present are combined with parallel browser signals such as cookies, and Google says conversions previously under-counted in those setups are recovered. With 1 May, a possible date for server-side conversion counts to rise with no site change.
  - 9 July 2026: containers loaded through the unsupported `/gtag/js` or `/gtag/destination` paths used to fall back to Google-provided tags and variables only. From this date the loading ID decides: a `GTM-` ID runs every tag, while `G-` and `AW-` IDs still run only Google tags. On a `GTM-` container loaded this way, custom HTML and third-party tags that had not been running can start on this date, a possible first-change date for an over-count or for new tags firing. `G-` and `AW-` loads are unchanged.
  - 8 October 2026: config commands surface in the dataLayer as `gtag.config` events, and wildcard custom event triggers may fire on them. The same note says every `gtm.js` snippet now initialises on container load regardless of `gtag('config')` commands, and Google's linked guide says `gtm.js` snippets will stop recognising or waiting for them. Likely effect, to confirm live: settings passed only through `gtag('config')` on a page that loads `gtm.js` (for example a server container URL or cookie settings) stop applying; check the collection request host and the cookies set. Google gives two routes: move the page to the `gtag.js` snippet, or keep the GTM snippet and apply the configuration in the GTM interface.
  - 12 October 2026: an optional container upgrade lets a container send data directly to Google destinations without loading `gtag.js`, and moves container-level Google settings to a new Settings tab. The user reviews and submits it, so it appears as a publish. In an upgraded container, read the Google tag settings (including any server container URL) live from the Settings tab and the destinations; do not assume they sit in a Google tag.
- Duplicate senders: a hardcoded gtag snippet, a CMS or platform plugin, GTM, enhanced measurement, a server route, Measurement Protocol and a gateway all able to send the same event.
- Other senders in the path (verify live):
  - Shopify customer events. Shopify says `checkout_completed` fires once per checkout, typically on the Thank you page; with upsell or post-purchase offers it fires on the first upsell offer page instead and not again on the Thank you page; if that page fails to load, it does not fire at all. A tag tied to the Thank you page misses upsell checkouts, and a failed page load loses the event for good.
  - Segment Protocols. With Schema Configuration set to Block Event, Segment drops unplanned events and, under the advanced controls, events that violate the tracking plan's JSON schema. Segment says blocked events not forwarded to another source are permanently discarded. A renamed event that is not in the tracking plan then stops at Segment, before any destination sees it.

### Tests

1. Read the live version and when it went live. The GTM UI Versions page keeps the publish history: entries with a date in the Published column, and who published. The API (`GET .../versions:live`, `tagmanager.readonly`) returns the live `containerVersionId` and its contents but no publish or creation time; `fingerprint` is recomputed whenever the version is modified, so never read it as a date. Without UI access, compare the live `containerVersionId` with one recorded at a known earlier time (the record, a change ticket, a monitoring snapshot).
2. If the live version did not change on the first-change date, the break is outside the published container, not necessarily in the app. Check, in order:
   - the app or page diff for the release;
   - GA4 property change history (Admin, Property change history, Editor role) for data streams, Google tag settings and enhanced measurement;
   - CMP and vendor script releases served from their own CDNs;
   - any Google tag gateway or CDN route serving the tag;
   - the GTM and GA4 release notes for the first-change date.
3. Check for unpublished changes in every workspace. List the container's workspaces (`workspaces.list`, `tagmanager.readonly`), then call `getStatus` on each; it reports one workspace's changed entities and merge conflicts. Record the workspace IDs checked.
4. Confirm which snippet, container and environment the production page loads: the request path (`/gtm.js` or `/gtag/js`), the ID and the environment parameters against the container's Live snippet. Then check that the collection request carries the values the config was meant to set, for example the server container host.
5. Preview the failing journey and a passing comparison journey. In Tag Assistant, read the event timeline, the Data Layer and Variables tabs, and for the tag under Tags Not Fired, which trigger condition failed.
6. Diff the release for dataLayer pushes and for any class, ID or attribute that a trigger or variable reads.
7. Other senders, where present. Shopify: run one test order through the store's test payment route with and without a post-purchase offer and count `checkout_completed` for each. Segment: read the source's Schema Configuration and its violations for the first-change date, and whether blocked events are forwarded to another source.

### Evidence to save

Live version ID, its publish time and the source of that time (Versions page, or the recorded IDs); the workspace IDs checked and their status; the loaded snippet path and ID; the preview event list for the failing step; the failed trigger condition; the diff lines that changed the name, key or selector.

### Who fixes it

App engineering owns the dataLayer contract and markup. The GTM owner owns triggers, tags and publishing.

### Traps

- Widening the trigger to a regex that accepts both names fixes today and hides the next rename. Restore the contract and add a regression test.
- Publishing a fix mid-incident without a version note makes the retest ambiguous.
- GTM Preview does not stop tags from sending to live destinations. Preview runs still need the test-traffic rules.

## Layer 4: browser request

### What breaks

- Tracker and ad blockers, privacy browsers and extensions blocking the request or the tag library. This is a share to size (test 6) and report, not a fault to fix.
- Content Security Policy. Google lists `img-src` and `connect-src` for measurement requests and `frame-src` for the service worker transport, each allowing the server container URL. The tag library host also has to be allowed to load.
- The page navigates away before the request finishes, typically a form that redirects on success. The request shows as cancelled or never starts.
- Identity lost between pages: cookie domain or prefix differences (Google recommends one cookie prefix across the site), cookies set by script capped by Safari. WebKit deletes cookies created in JavaScript and other script-writable storage after 7 days of Safari use without interaction with the site.
- Safari 26 also stops scripts on Apple's known-fingerprinting list from setting long-lived script-written storage such as cookies or LocalStorage (WebKit's Safari 26.0 post). Apple does not publish the list, so compare the analytics cookie's expiry in Safari with other browsers.
- Safari 27 identity resolution blocking (WebKit post of 19 October 2026). Safari in the 27 releases blocks network access, by domain, to third-party services that collect email addresses at login or signup and tie them to a tracking ID. A signup or login tag that sends email addresses to such a service can be blocked there. Report the Safari 27 share as a measurement limit and do not route around it. Reports of IP-based blocking of ad endpoints stay unverified.
- A collection subdomain that Safari treats as cloaked. WebKit's tracking prevention page says Safari detects third-party CNAME cloaking and third-party IP address cloaking and caps cookies set in those HTTP responses at 7 days. A `collect.example.com` that resolves to another provider's addresses can fall under it while the main site does not (verify the current rules live).
- Google tag gateway for advertisers. The tag and its measurement requests are served from a reserved path on the site's own domain, forwarded by a CDN, load balancer or web server. A missing routing rule after a site, CDN or load balancer change, or a path the site later reuses, breaks collection while the page itself looks fine. Possible first-change dates from the GTM release notes: Google Cloud integration in Beta on 5 January 2026 and generally available on 1 June 2026; Akamai on 29 January 2026; automatic CDN zone detection and routing rules for Akamai and Fastly on 14 May 2026; CloudFront through Tag Assistant on 3 June 2026.
- Oversized cookie headers that make the collection endpoint reject the request. The status code shows it.
- Batching. One collection request can carry several events, so count events in the payloads, not requests.

### Tests

1. DevTools network panel with Preserve log and cache disabled. Filter by the collection host or `collect` (or the gateway path). For the failing step, read status, initiator and payload: the measurement ID, the event names and their parameters. Count each event across all requests; one request may hold several.
2. Console: CSP violation messages name the blocked directive and host.
3. Storage: the analytics cookies' domain, expiry and whether they persist across pages of the journey.
4. Collection subdomain: compare its address records with the main site's.

   ```bash
   for h in www.example.com collect.example.com; do
     echo "$h"; dig +short "$h" CNAME; dig +short "$h" A; dig +short "$h" AAAA
   done
   ```

   Then in Safari Web Inspector read the `Set-Cookie` expiry on a response from the subdomain. A 7-day expiry where the server sets a longer one points at Safari's cloaking defence, not at the server.
5. Gateway, if used: open `https://www.example.com/<measurement path>/healthy`; Google's setup guide expects `ok`. Check that the tag script loads from that path and, in Tag Assistant, that hits are sent to it. Confirm the CDN or load balancer rule still exists after any site or CDN change.
6. Size browser policy and blocker loss from data, not from one browser. Split the ratio by browser family (Chrome against Firefox and Safari) for the baseline and the incident; Brave usually reports as Chrome, so it hides in the Chrome row. Where the site serves a first-party endpoint (gateway or server container), compare that endpoint's request logs with GA4 events for the same days. Turning a blocker on and off in one browser shows only that the request can be blocked.

### Evidence to save

A redacted HAR or screenshot of the failing request with status, the per-event count across requests, the CSP message text, the DNS records, the cookie expiry seen in Safari, the gateway health response, the ratio by browser family, and cookie values for test visits only.

### Who fixes it

Web engineering; the security owner for CSP changes; the CDN or load balancer owner for gateway rules. Blocked traffic, including Safari 27's identity resolution blocking, is a measurement limit to report, not a bug to route around.

## Layer 5: server container

### What breaks

- The route: DNS, TLS, CDN or load balancer. A same-origin path caught by the website framework returns the site's HTML with status 200, which looks healthy and collects nothing.
- The web side still points at an old, staging or default server URL. For gtag.js the setting is `server_container_url`. In web GTM it sits in the Google tag configuration, or, in a container upgraded under the 12 October 2026 change, in the container's Settings tab or destinations (verify the current label live).
- A `gtm.js` page that set the server URL only through `gtag('config')` (layer 3, 8 October 2026). Likely effect, to confirm live by reading the collection request host: measurement goes straight to Google and the server container sees nothing.
- No client claims the request (path or protocol mismatch), or the wrong client claims it. Clients run in priority order, highest first, and claiming stops later clients from running.
- A payload the claiming client does not parse: a field renamed, a nested structure flattened, a number sent as a formatted string (`"$79.00"` where `79.00` is expected).
- Transformations remove a parameter a tag needs, or a later rule reintroduces one an earlier rule removed.
- Server triggers keyed on fields the release changed: page hostname or path, event name, client name.
- Web and server releases that depend on each other. A server tag that needs a parameter the published web container does not send yet, or the reverse, fails until both are live.
- Destination credentials expired or revoked.
- Hosting: outage, instance limits, an unpaid hosting plan. All events stop at once, and the first symptom is often conversions quietly vanishing.
- Preview is not production. Google's manual setup deploys exactly one preview server, separate from the tagging servers, which proxy preview requests to it. A passing server Preview proves the preview path, not the tagging servers' config, version or instances.
- A new server version not yet published, or still propagating to every instance after publishing. Wait and retest before declaring a publish failed.
- Identity and context: Google says that if server-side tagging manages a separate client ID, all measurements for the stream must flow through the server. The server's own IP is not the user's: `ip_override` in common event data carries the user's IP, and geography and IP-based internal-traffic rules depend on what is forwarded.
- Consent: the Google tag adds consent parameters to the request and Google's server tags respect them. Third-party server templates need their own check. Google Ads conversion tags in the server container need the server-side Conversion Linker.
- A Google tag gateway is not a server container. If the site uses both, test each route separately (layer 4 for the gateway).
- Browser plus server sends to one ad platform (Meta Pixel and Conversions API; TikTok and LinkedIn have similar routes). Meta deduplicates a browser and a server event only when the Pixel's `eventID` matches the Conversions API `event_id` and the event names match, within 48 hours of the first event with that ID. Generate the ID once per outcome and pass it to both sends; two independently generated IDs double count. Read other platforms' deduplication keys live.

### Tests

1. Health: `curl -s -o /dev/null -w '%{response_code}\n' https://collect.example.com/healthy`. Google's manual setup guide uses `/healthy` on the tagging servers and expects 200; hosted providers may add their own checks.
2. Browser: the measurement request goes to the server URL, not directly to Google, in the published configuration.
3. Server Preview with web Preview open: Incoming HTTP Request (path, query and body), the Client box (which client claimed), Event Data, fired tags, and the outgoing request with its response status and body. Put the incoming body next to the format the claiming client expects.
4. Non-browser senders (webhooks, backends) do not appear in server Preview by default. Stape documents an `X-Gtm-Server-Preview` header copied from server Preview, menu Send requests manually; the value changes over time.
5. Production, from the tagging servers, not Preview: request logs and outgoing error counts for the event since the first-change date. On Cloud Run, the Cloud Logging MCP server's `list_log_entries` reads them; on a hosted provider, use its logs.
6. Both live versions: the web and the server container's live version IDs and publish times. A server publish that needs a web change, or the reverse, shows as one side going live without the other.
7. The destination's own diagnostics for the test event: GA4 Realtime or DebugView, the ad platform's event diagnostics.
8. Meta, where that route exists: in Events Manager, the event's Event Deduplication tab for the incident dates (deduplication rate and how often each source sends each key), then one test event through the Test Events tool. Meta says events sent with `test_event_code` are not dropped and are used for targeting and ads measurement, so send them only under the test-traffic rules.

### Evidence to save

Incoming path and body shape, claiming client name, event data keys (not values that identify a person), outgoing status and response, tagging-server log counts before and after the first-change date, both live version IDs with publish times, and, for a Meta route, the deduplication rate per source for the incident dates.

### Who fixes it

The server container owner, the hosting account owner (billing and plan), the owner of each destination credential. Once this layer is proven to fail, hand detailed work to server-side-tracking-debug if it is installed; otherwise continue with the tests above and Google's server-side debug guide.
