d8a .tech
Data Analytics

How to Duplicate GA4 Requests Without Touching Your Tracking Setup

Divine Data Team
#analytics#GA4#GTM#server-side#open-source
Feature image

You want to run a server-side GTM container alongside your existing GA4 setup. Maybe you’re evaluating d8a.tech, maybe you’re building a custom analytics pipeline, or maybe you just want data redundancy so Google isn’t your single point of failure. Whatever the reason, the goal is simple: take the same tracking data you’re already collecting and send a copy to a second endpoint.

The problem is that “simple” depends entirely on how your tracking is implemented. If you’re using gtag.js directly, you’re in luck. If you’re using Google Tag Manager, you’re about to discover a maintenance nightmare. And that’s exactly why we built an open-source library to solve it.

Why We Built This

When we started building d8a, we wanted onboarding to be dead simple. New users should be able to send data quickly and immediately see why our data structure is easier to work with than standard GA4 exports.

That is why, alongside our own web tracker, we made it possible to duplicate existing GA4 requests. We like the gtag API and saw no reason to reinvent the wheel for teams that still want to keep GA4 in place. You keep your current setup, mirror the same traffic to d8a, and evaluate the results with almost zero implementation risk.

The Easy Case: gtag.js

When you’re using gtag directly on your site, duplication is straightforward. You add a second config call with a different Measurement ID pointing to your server-side container:

<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  // Original GA4 property
  gtag('config', 'G-XXXXXXX');

  // Server-side container with a different Measurement ID
  gtag('config', 'G-YYYYYYY', {
    server_container_url: 'https://sst.example.com'
  });
</script>

The different Measurement ID prevents double-counting, and server_container_url tells the second config where to send data. Done. Two destinations, one tracking setup.

But most production sites don’t use raw gtag. They use Google Tag Manager.

The GTM Problem

In Google Tag Manager, there’s no equivalent shortcut. Each event tag must be assigned to a specific Google Tag. To send the same events to two destinations, you’d need to:

For a setup with a handful of events, this is annoying. For a setup with dozens of event tags, custom dimensions, and carefully tuned triggers, this is a maintenance nightmare. Every change needs to be made twice. Every QA cycle doubles. And when someone inevitably forgets to update both copies, your data diverges silently.

There had to be a better way.

The GA4 Duplicator

The GA4 Duplicator is a lightweight script that intercepts GA4 network requests at the browser level and copies them to a second endpoint. It doesn’t care about your tag configuration. It doesn’t know about your events, triggers, or variables. It operates one layer below all of that.

Under the hood, it monkey-patches the four mechanisms GA4 uses to send data: fetch, XMLHttpRequest, navigator.sendBeacon, and script src assignments. When any of these fires a request to Google’s collection endpoint, the duplicator copies that request and sends an identical one to your server-side container.

The setup is minimal:

<script src="https://cdn.jsdelivr.net/npm/@d8a-tech/gd/dist/gd.min.js"></script>
<script>
window.createGA4Duplicator({
  server_container_url: "https://sst.example.com/g/collect"
});
</script>

In GTM, create a Custom HTML tag with this code, then open your Google Tag and configure Tag Sequencing there so the duplicator fires before it. No separate triggers needed on the duplicator tag itself - it piggybacks on the Google Tag’s firing schedule and silently copies every request that follows.

One important detail: the duplicator copies requests as-is, including the original Measurement ID. To avoid double-counting in your server-side container, override the Measurement ID in the server-side tag configuration. The duplicated requests arrive with your original G-XXXXXXX ID, and your server-side container should map them to the correct destination.

What We Had to Solve Under the Hood

A few things weren’t obvious when building this.

CORS. Duplicated requests go to a different origin than the original Google endpoint. To make cross-origin requests work correctly, the duplicator attaches the richsstsse parameter to each copied request. Without it, the server-side container rejects the request before it even reaches your tags.

Cookies and debug mode. GA4’s debug mode reads cookies from the incoming request to identify the debug session. The duplicator sends cookies along with each duplicated request so that debug mode works out of the box on your server-side container. When you’re testing in GTM preview mode, the duplicated traffic shows up in your server-side container’s debug view - exactly as you’d expect.

These are the kinds of details that take hours to figure out when you’re building a custom solution. The library handles them for you.

Beyond d8a

The library is generic. It has zero dependencies on d8a and works with any server-side GTM container or any endpoint that accepts GA4-formatted requests.

Some use cases beyond d8a:

The source code is on GitHub and the package is available on npm.


If you’d like to see how the duplicator works with d8a.tech, check the detailed setup guide. The library is open source, the setup takes five minutes, and your existing tracking doesn’t change at all.

← Back to Blog