Move your tags
off the browser
sGTM is Google's server-side tag management solution that processes tag logic, event collection and data forwarding on a secure, scalable server — not in the visitor's browser. This fundamentally changes the data flow: instead of a browser executing 20+ third-party scripts, a single lightweight client-side hit is sent to your own cloud endpoint, which then fans out to all downstream vendors.
europe-west1 (Belgium) or europe-west2 (London). Min instances: 1 (prevents cold starts). Set memory to 512Mi minimum.gtm.yourdomain.com. This is critical — the endpoint must be first-party to avoid ad blockers. Safari ITP treats third-party origins differently; a first-party subdomain ensures cookies set by the server are classified as first-party.server_container_url set. This routes all gtag hits to your server first.RUN_AS_PREVIEW_SERVER=true for a dedicated debug endpoint. Never use production for preview — it leaks internal tag config via debug panels.
Media Vendor
Configuration Guide
Each vendor requires specific server-side tag templates, API credentials, identity signals, and consent gates. Below is the comprehensive configuration reference for all 8 major platforms.
AW-XXXXXXXXX) + Conversion Label per actionsha256_email_address variable. Works with or without cookie match. Dramatically improves conversion modelling under ITP/consent loss._gcl_aw cookie from the HTTP request headers and passes GCLID to tag. Set cookie via sGTM response headers to extend lifetime to 400 days (1P domain).event_id on both browser pixel AND server tag. Meta deduplicates using event_name + event_id within 48h window. Without dedup, events are double-counted.data_processing_optionscbuid parameter in some integrations.https://s.amazon-adsystem.com/iu3?pid=&event=...eupubconsent-v2 or euconsent-v2) and appends to the Amazon pixel call.https://business-api.tiktok.com/open_api/v1.3/pixel/track/event_id on both browser pixel and server tag. TikTok deduplicates within a 48h window per event_name + event_id.https://ads-api.reddit.com/api/v2.0/conversions/events/{account_id}conversion_id to deduplicate browser pixel vs server events. 24h dedup window.POST /v1/integrations/sdk/track — eventsPOST /v1/integrations/sdk/user/update — profileAPP_ID:DATA_API_KEY (base64 encoded)https://api-eu.moengage.com. Always use the correct regional endpoint for GDPR compliance.https://api2.amplitude.com/2/httpapi (standard) or /batch for high volumeapi_key field. No header auth.session_id is a Unix timestamp. sGTM must read the Amplitude session cookie (AMP_*) or generate/pass from the web SDK. Without session_id, funnel analysis breaks.https://api2.appsflyer.com/inappevent/{app_id}authentication header| Vendor | sGTM Template | TCF 2.0 | Dedup Required | Key Identity Signal | Consent Mode v2 |
|---|---|---|---|---|---|
| Google Ads | Native | No | No | GCLID / SHA256 Email | ad_storage + ad_user_data |
| Meta CAPI | Community | No (own framework) | YES — event_id | fbp + fbc + SHA256 Email | ad_storage |
| Amazon DSP | None — Custom | REQUIRED | No | Amazon Ad ID (opaque) | Custom TC string param |
| TikTok | Community | No | YES — event_id | ttclid + _ttp + SHA256 | ad_storage |
| None — Custom | No | YES — conversion_id | _rdt_uuid + SHA256 Email | ad_storage | |
| moEngage | None — Custom | No | No (idempotent) | unique_id (CRM ID) | analytics_storage |
| Amplitude | Community | No | YES — insert_id | user_id + device_id | analytics_storage |
| AppsFlyer S2S | None — Custom | No | No | appsflyer_id (on-device) | n/a (supplemental only) |
CMP Configuration
& OneTrust Integration
Consent Management Platforms are the gatekeeper between user preference and data collection. In an sGTM architecture, consent must be correctly captured client-side and honoured both in the browser (blocking tags) and server-side (gating outbound vendor calls).
ad_storage + ad_user_data + ad_personalization. "Performance Cookies" → analytics_storage.denied before the CMP loads. This prevents any tag from firing before consent is obtained — required for GDPR compliance.eupubconsent-v2 cookie. This is automatically updated when user changes consent preferences. sGTM reads this cookie from incoming request headers.x-ga-gcs parameter (Google Consent String) to read the compact consent representation.Alternatively, read the
eupubconsent-v2 cookie directly from the Cookie header of the incoming HTTP request for TCF-required vendors (Amazon DSP).
x-ga-gcs or parses the event model's consent object. Use this as a trigger exception — if ad_storage is denied, block the advertising vendor tags from firing.This provides a true second layer of consent enforcement, independent of the browser.
GCP Microservice
Consent Platform
For enterprises requiring full control over consent data, custom audit trails, or multi-jurisdiction logic too complex for commercial CMPs, a GCP-native microservice CMP is the gold standard. This gives you a consent graph stored in your own cloud, versioned, queryable, and integrated directly with sGTM.
Limitations,
Gotchas & Gaps
sGTM is powerful but not a silver bullet. Understanding where it breaks down — and why — is essential to designing a robust enterprise implementation. Below is an exhaustive reference of known limitations organised by category.
- No
fetch()— usesendHttpRequest()API - No
setTimeout— userunAsyncTask() - No access to
process,fs, or Node globals - No npm packages — only approved Sandboxed JS APIs
- Template permissions model — must declare all external URL calls in template metadata
- Only works when the sGTM endpoint is on a first-party subdomain of your site
- Cookies set via sGTM cannot access
HttpOnlycookies set by your app backend on the same subdomain (separate origin) - Safari still partitions cookies by domain — first-party status must be maintained
- iOS 17+ private relay hides IP, breaking geo-IP-based logic
- Chrome CHIPS partitioned cookies: cross-site iframe scenarios become complex
- Must pass
gdpr=1andgdpr_consent={{TC_STRING}} - TC string must encode Purposes 1, 3, 4, and Legitimate Interest claims
- String must be generated by a registered CMP (IAB registered list)
- OneTrust, Cookiebot, Quantcast all qualify
- Custom CMPs must register with IAB Europe CMP list
- SKAN (SKAdNetwork) requires native iOS SDK integration
- Google Play Referrer requires Android SDK
- Deep link attribution requires SDK
- AppsFlyer kit wrapper (mParticle/Segment) needs SDK on device
- S2S API only supplements — cannot attribute installs server-side
- DSR must go direct to AppsFlyer Privacy Cloud (not via sGTM)
- Solution: Set
--min-instances=1in Cloud Run config - Cost: ~$18/month per min instance (europe-west2, 512Mi)
- For >100k events/day: consider min-instances=2 for redundancy
- Preview server: separate instance, also keep warm
- If browser pixel AND server tag both fire, events are counted twice
- Must pass identical
event_idfrom both sources - Generate event_id client-side, push to dataLayer, forward via GA4 event
- Dedup window is 48h — late S2S events outside window are NOT deduped
- Event name must also match exactly (case-sensitive)
- Segment Analytics.js events cannot be natively parsed without custom client
- Must build custom client to parse Segment Track/Page/Identify payloads
- mParticle server-to-server events require custom HTTP client in sGTM
- Workaround: use Segment Functions or mParticle Forwarding Rules as parallel path
- Must activate both web container AND server container preview simultaneously
- Preview URL requires
?gtm_debug=xparameter AND server preview token - Cloud Run logs (stderr) show errors but with up to 30s delay in Log Explorer
- Third-party tag errors appear as HTTP response codes only — no JS stack traces
- Solution: build a local sGTM mock environment using Docker for dev testing
- sGTM reads consent state from event payload — if browser sends cached consent, server fires
- Custom GCP CMP with real-time Firestore lookup closes this gap (see Tab 04)
- Commercial CMPs rely on cookie state — up to 30-min lag on withdrawal propagation
- Backend data pipelines (BigQuery) may process events before withdrawal is applied
- Best practice: apply consent filtering at query time as well as collection time
- If your site uses a CDN (Cloudflare, Fastly), the IP may be the CDN edge IP
- Must configure CDN to pass
X-Forwarded-FororX-Real-IPheaders - sGTM reads
x-forwarded-forheader — use sGTM variable to extract original IP - iOS 17+ Private Relay: Apple proxy IP replaces real IP → geo-detection breaks
- MaxMind GeoIP2 or Google's IP geolocation API required for city-level accuracy
- Data thresholds applied to reports when user counts are low (<50 in some segments)
- Exploration reports: max 10M events per query
- Realtime report: up to 30 min delay for server-side events
- BigQuery export: up to 24h delay; not suitable for real-time dashboards
- Custom dimensions capped at 50 event-scoped, 25 user-scoped
- Community templates are NOT reviewed by Google for security
- A compromised community template could exfiltrate data to attacker servers
- Best practice: fork community templates, audit the code, host internally
- Block external URL calls in template permissions to only your approved domains
- Review template code on every update before deploying to production
| Vendor | TCF 2.0 Required | Consent Framework | Denied Mode Behaviour | Data Residency Option |
|---|---|---|---|---|
| Google Ads | Not required | Consent Mode v2 | Cookieless pings + conversion modelling | EU data processing terms |
| GA4 | Not required | Consent Mode v2 | Cookieless pings, ML modelling | EU-only data region |
| Meta CAPI | Recommended for EU | Meta's own + LDU | Limited Data Use (LDU) flag | EU data processing |
| Amazon DSP | MANDATORY in EU | IAB TCF 2.0 | Event silently dropped | EU hosted (check contract) |
| TikTok | Recommended | Own privacy policy | Limited data mode flag | EU/US data centres |
| Check DPA | Own privacy policy | No events sent | US only currently | |
| moEngage | Not required | GDPR processor | Do not send events | EU data centre (api-eu.) |
| Amplitude | Not required | GDPR processor | Do not send events | EU isolation flag |
| AppsFlyer | For DSR processing | Privacy Cloud | S2S events not sent | EU data residency |
• Server-authoritative event confirmation
• Cookie lifetime extension to 400 days
• Real-time data enrichment from CRM
• Reducing browser payload (remove 3P scripts)
• First-party cookie management
• Multi-vendor fan-out from single event
• Consent-gated vendor forwarding
• Real-time personalisation — propagation latency
• High-frequency events (>10M/day) — cost scales
• Consent withdrawal — needs custom CMP lookup
• Amazon DSP — TCF string complexity
• Community templates — security audit required
• CDN IP masking — geolocation accuracy
• Multi-region latency — deploy per region
• Real-time audience building (use CDP instead)
• Replacing full CDPs (mParticle/Segment)
• Server-side rendering personalisation
• Email / offline touchpoint attribution
• Sub-100ms latency requirements
• Complex ML model inference
• Long-running background jobs
Top 5 Ecommerce Events
Step-by-Step Integration
The five most commercially critical ecommerce events — view_item, add_to_cart, begin_checkout, purchase, and refund — each have distinct field requirements, dedup strategies, and downstream configurations per vendor. This tab walks through each event end-to-end: dataLayer push → sGTM variable mapping → vendor-specific payload for all 8 platforms.
view_item event from your frontend. This is the single source of truth that feeds all downstream vendor mappings via sGTM.view_item conversion action OR standard remarketing event'product' (hardcoded)view_item AND ad_storage = granted"ViewContent""product"event=productView in pixel URLitem_id as custom param if using AMC attribution"ViewContent""product""GBP""ViewContent""product_viewed""Product Viewed""af_content_view""product""GBP"'cart'"AddToCart""product""GBP"event=addToCart"AddToCart""product""GBP""AddToCart""GBP""add_to_cart""Product Added""af_add_to_cart""GBP"'checkout'"InitiateCheckout""GBP"event=checkout"InitiateCheckout""GBP""Lead" (Reddit has no CheckoutStart event)"GBP""checkout_started""Checkout Started""af_initiated_checkout""GBP""Purchase""pur_ORDER-ID""product""GBP"event=purchase"CompletePayment""GBP""Purchase""GBP""order_completed""Order Completed"$revenue (dollar sign prefix) to populate Amplitude's Revenue chart automatically. Also set user_properties.$set: {ltv: updated_ltv} to update lifetime value."af_purchase""GBP""Refund" or suppress original purchase from ROAS reports via offline event set correction."Refund", value: negative, order_id for correlationevent: "Refund" for tracking but this won't auto-deduct from ROAS metrics in TikTok Ads Manager."Refund" for your own analytics. Exclude hashed emails of refunders from Reddit retargeting audiences manually."order_refunded" — fully supported"Order Refunded""ref_" + transaction_id-49.99"Refund"af_purchase with negative af_revenue value to correct cohort LTV.-49.99_refund suffix| Vendor | view_item | add_to_cart | begin_checkout | purchase | refund |
|---|---|---|---|---|---|
| Google Ads | Remarketing: product | Remarketing: cart | Remarketing: checkout | Conversion Tag | API only |
| Meta CAPI | ViewContent | AddToCart | InitiateCheckout | Purchase | Custom event |
| Amazon DSP | productView | addToCart | checkout | purchase | Not supported |
| TikTok | ViewContent | AddToCart | InitiateCheckout | CompletePayment | Custom event |
| ViewContent | AddToCart | Lead (proxy) | Purchase | Not supported | |
| moEngage | product_viewed | add_to_cart | checkout_started | order_completed | order_refunded ✓ |
| Amplitude | Product Viewed | Product Added | Checkout Started | Order Completed | Order Refunded ✓ |
| AppsFlyer S2S | af_content_view | af_add_to_cart | af_initiated_checkout | af_purchase | af_purchase (neg) |