Google Analytics 4

GA4 without gtag.js

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 caps client-side JavaScript cookies at 7 days. Your _ga cookie expires weekly, 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.

1

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.

2

Server generates client_id

The Ingestion Gateway generates a GA4-format client_id as {random_uint32}.{unix_timestamp} — the exact same format Google uses.

3

Server sets _ga cookie

The _ga cookie is set via Set-Cookie header with a 400-day expiry. Because it is server-set on your first-party subdomain, it is fully exempt from Safari ITP restrictions.

4

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.

5

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.

6

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.

Pipeline as Code

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.yamlYAML
# 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.yamlYAML
# 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: true

Full 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 via a Set-Cookie HTTP header from your own subdomain, Safari ITP does not rotate it after 7 days the way it does with gtag-set _ga cookies.
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 and Safari users are not double-counted as new visitors after each 7-day cookie cap. Engagement metrics will be more accurate because session continuation works across 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.

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.