Google Analytics 4
GA4 without gtag.js
Google Analytics 4 server-side tracking, done properly. Deliver events to GA4 via Measurement Protocol with a self-generated client_id, server-set _ga cookie, and ITP-exempt 400-day expiry. Full GA4 reporting with zero Google JavaScript in the browser.
The problem with gtag.js
Google's client-side tracking has four fundamental problems that cannot be fixed with configuration changes. They are architectural limitations.
80KB+ Payload
gtag.js loads 80KB+ of JavaScript before a single event fires. Combined with GTM, total payload regularly exceeds 200KB — directly impacting Core Web Vitals and conversion rates.
ITP-Limited Cookies
Safari's Intelligent Tracking Prevention deletes JavaScript cookies after 7 days if the visitor doesn't come back. A week away and your _ga cookie is gone, fragmenting user sessions and inflating visitor counts.
Ad Blocker Blocked
EasyList blocks google-analytics.com and googletagmanager.com by default. 30-40% of technical audiences never register a single event. Your data has a permanent blind spot.
Consent Race Conditions
Client-side consent banners race against tag execution. gtag.js may fire before consent is granted, or consent may be revoked after events are already in flight.
How server-side GA4 works
Six steps from browser event to GA4 report. No Google JavaScript involved.
Datafly.js collects events
The 5.2KB collector captures page, track, and identify events via sendBeacon. No gtag.js, no client-side tag manager container, no 80KB+ payload.
Server generates client_id
The Ingestion Gateway generates a GA4-format client_id as {random_uint32}.{unix_timestamp} — the exact same format Google uses.
Server sets _ga cookie
The _ga cookie is set via Set-Cookie header with a 400-day expiry. Because it is server-set from your own domain, Safari's 7-day limit does not apply.
Pipeline Engine transforms to Measurement Protocol
Your YAML pipeline config maps event properties to GA4 Measurement Protocol parameters. Session data, user properties, and engagement metrics are enriched automatically.
Delivery Worker POSTs to GA4
A purpose-built delivery worker sends the formatted payload to google-analytics.com/mp/collect with your Measurement Protocol API secret. Exponential backoff retry on failure.
GA4 reports populate normally
Events appear in your GA4 property exactly as if they came from gtag.js. Real-time reports, explorations, BigQuery export — everything works as expected.
Transformation configs you can read
Every GA4 event mapping is defined in version-controlled YAML. Review in pull requests, roll back with a commit revert.
# ga4-pageview.yaml — Page view to Measurement Protocol
name: ga4_pageview
integration: google-analytics-4
trigger:
event: page
conditions:
- field: context.page.url
operator: exists
transform:
mapping:
- source: properties.title
target: page_title
- source: context.page.url
target: page_location
- source: context.page.referrer
target: page_referrer
- source: context.locale
target: language
enrichments:
- type: session
fields: [session_id, session_number]
- type: vendor_id
vendor: ga4
cookie: _ga
output:
format: ga4_measurement_protocol
event_name: page_view
endpoint: https://www.google-analytics.com/mp/collect
validate: true# ga4-purchase.yaml — E-commerce purchase event
name: ga4_purchase
integration: google-analytics-4
trigger:
event: track
conditions:
- field: event
operator: equals
value: Order Completed
transform:
mapping:
- source: properties.order_id
target: transaction_id
- source: properties.revenue
target: value
- source: properties.currency
target: currency
default: USD
- source: properties.products
target: items
transform: array_map
fields:
- source: product_id
target: item_id
- source: name
target: item_name
- source: price
target: price
- source: quantity
target: quantity
- source: category
target: item_category
enrichments:
- type: session
fields: [session_id, session_number, engagement_time_msec]
- type: vendor_id
vendor: ga4
cookie: _ga
output:
format: ga4_measurement_protocol
event_name: purchase
endpoint: https://www.google-analytics.com/mp/collect
validate: trueFull GA4 feature support
Everything you use in GA4 today works with server-side delivery. No compromises, no missing features, no manual workarounds.
- Page views
- Custom events
- Session tracking
- Engagement time
- Scroll tracking
- E-commerce: view_item
- E-commerce: add_to_cart
- E-commerce: begin_checkout
- E-commerce: purchase
- User properties
- Cross-domain measurement
- Debug mode validation
- BigQuery export compatibility
- Enhanced measurement events
- Consent mode integration
BigQuery Export Compatibility
Events delivered via Measurement Protocol appear in your BigQuery export with the same schema as client-side events. Your existing queries, dashboards, and ML pipelines continue to work without modification.
Frequently asked questions
- How is the GA4 client_id generated server-side?
- The Datafly Ingestion Gateway generates a client_id matching GA4's expected format ({random}.{timestamp}) on first visit and persists it in a 400-day server-set first-party cookie. The same client_id is used on every Measurement Protocol call for that visitor. Because it is set by your own server, Safari doesn't delete it after 7 days the way it does the _ga cookie set by gtag.js.
- Are GA4 sessions managed server-side too, or just the client_id?
- Both can be. The client_id is always server-side, and each pipeline chooses where sessions are assigned. With server sessions (the default for new web pipelines) Datafly Signal assigns session_id and session_number on your own first-party collection domain and emits session_start and first_visit once per session; with browser sessions the Datafly Signal collector does the same in the browser. Either way the inactivity window is GA4's 30 minutes by default and can be set to match your GA4 property (a sliding window, not a cap on session length), and every event carries those session fields to your own first-party collect endpoint, from which Datafly Signal delivers them to GA4 server-side. Session starts and first visits therefore reach GA4 even where ad blockers stop gtag.js from loading, and they arrive with the same server-held client_id, so a Safari visitor who comes back after a week is not given a new client_id.
- Will GA4 reports look the same as with gtag.js?
- For most metrics yes, with two important differences. Reported users will be slightly higher because ad blocker traffic is recovered, while Safari users who come back after a week keep the same client_id instead of being counted again under a new one. Returning-user metrics will be more accurate because that client_id lasts for the full 400-day cookie window. Page view counts and event counts should match within a few percent during parallel running.
- Does Datafly Signal support Consent Mode v2 for GA4?
- Yes, server-side. ad_user_data and ad_personalization signals are evaluated at the Signal Core based on your CMP's consent state and attached to every Measurement Protocol payload. ad_storage and analytics_storage are intentionally not sent because Measurement Protocol does not honour them. The result is Consent Mode v2 enforcement with a per-event audit trail.
- Can I run server-side GA4 alongside an existing gtag.js setup?
- Yes. Both run in parallel and both send events to GA4 — gtag.js client-side, Datafly Signal server-side. GA4 does not deduplicate Measurement Protocol events against gtag events, so during transition you would normally disable gtag for events that Signal is sending. Most teams migrate event-by-event, switching off the gtag pixel for each event as the server-side equivalent is verified.
- What about Enhanced Measurement events like scroll and outbound click?
- Datafly.js implements equivalents server-side: scroll depth, outbound link clicks, file downloads, video engagement, and form submissions. The events are sent to GA4 as standard event names (scroll, click, file_download, etc.) so they appear in the same reports as gtag-emitted Enhanced Measurement events. Configurable per pipeline.
Related
Google Ads
Same server-side pattern for Google Ads Enhanced Conversions with hashed PII matching.
Consent Mode v2
How ad_user_data and ad_personalization are evaluated server-side per event.
Safari ITP Recovery
400-day first-party cookies for client_id stability across the full buying cycle.
Page Performance
Removing gtag.js is one of the largest single page-weight wins available.
See server-side GA4 in action
Book a technical walkthrough where we show you GA4 events flowing from Datafly.js through the pipeline to your GA4 property in real-time. No gtag.js required.