tagora

Server-side GTM (sGTM): what it is, how it works, what it takes to run

A second GTM container, running on your infrastructure, that receives measurement data from the browser and decides what to forward to each vendor.

What it is

Server-side GTM is a second container that runs on a server instead of in the browser. It is Google's own container image, deployed on infrastructure you operate — usually Cloud Run, sometimes App Engine or your own Kubernetes — and reachable at a hostname on your domain, such as sgtm.example.com.

In a standard setup, each tag sends data straight from the visitor's browser to its vendor. GA4 posts to region1.google-analytics.com/g/collect, Meta to connect.facebook.net, and so on: one third-party request per vendor, each carrying the visitor's IP, user agent and referring URL by default.

With a server container, the browser sends once — to your endpoint. Your server then forwards to the vendors, from your infrastructure rather than the visitor's device. The browser stops talking to each vendor directly, and you gain a place to decide what each one receives.

How a request changes

Client-side, a GA4 event is a request from the browser to https://region1.google-analytics.com/g/collect?v=2&tid=G-XXXXXXX&en=purchase&….

With a server container, the same event goes to https://sgtm.example.com/g/collect with the same payload. Your endpoint receives it, runs the server container's own tags and clients, and issues the outbound calls to Google, Meta and anyone else you have configured.

The switch is a single setting: server_container_url on a Google tag configuration variable in your web container. Every tag using that variable moves at once.

Serving the container itself

The endpoint can do a second job, configured separately from the transport.

By default the GTM library loads from Google: https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX. A server container can serve that file from your domain instead, so both the library and the measurement data come from one hostname you control. This is enabled on the Web Container client and restricted by an Allowed Container IDs list, which should name your containers and nothing else — an endpoint left unrestricted will serve any container id it is asked for.

The two are independent, and most deployments use only the first. Across 1,939 sites we loaded in a real browser, 121 routed measurement through their own endpoint but only 48 also served the library from it. Which one you need depends on what you are trying to fix, below.

What it changes for cookie lifetime

This is the most common reason to deploy one.

Safari's Intelligent Tracking Prevention caps cookies written with document.cookie at 7 days, and at 24 hours when the visitor arrived on a link carrying tracking parameters. Analytics libraries set their identifiers exactly that way, so a returning visitor beyond that window counts as a new one and attribution windows quietly truncate.

Cookies set by a server, in a Set-Cookie response header, are not capped by the same rule. Routing measurement through your own endpoint lets the server issue the identifier, and it persists as intended.

This depends on the endpoint being a real subdomain of your site, served by infrastructure you operate. A CNAME pointing at a vendor's servers is resolved and treated as third-party, and the 7-day cap applies again.

What it changes for delivery

Content blockers and DNS blocklists work primarily on hostnames — google-analytics.com, connect.facebook.net, googletagmanager.com. Requests to your own subdomain are not on those lists, so measured collection rates rise after a migration.

Which part you move determines what this affects. Moving the transport changes where the data goes; the library is still fetched from Google, so a blocker matching googletagmanager.com prevents the container from loading at all and nothing runs. Serving the library from your endpoint as well is what changes that.

This is a technical consequence of the architecture, not a legal one. Consent obligations attach to the processing, not to the hostname, and an endpoint that is harder to block still needs the same lawful basis as one that is not.

What it does not change

Consent. Data still leaves the browser. You have changed where it goes first.

Visibility to the visitor. The request appears in DevTools like any other, on your hostname.

Who ultimately receives the data. Forwarding still happens; it originates from your server rather than the browser. Fewer vendors execute code on the page, which is a genuine benefit, but the recipients are the same until you change them.

The web container. Custom HTML tags still run in the visitor's browser exactly as before.

What running one involves

A server container is a service in your production estate, and carries the same operational work as any other.

Updates. Google publishes new container images; keeping current is ordinary maintenance for an internet-facing service that accepts input from every visitor.

A layer in front. The endpoint accepts POST requests from anywhere by design, so a WAF and rate limiting belong in front of it — both to keep the data clean and because compute is billed regardless of who sent the traffic.

Availability. Measurement now depends on your uptime rather than Google's.

Cost. Compute plus egress, scaling with traffic. Worth modelling before migrating, since the sites that benefit most are also the ones that send the most.

Where it runs. Visitor data transits infrastructure you choose, which makes region a decision you now make deliberately — useful if you have residency requirements, and something to answer for if you do not.

Outbound requests. Server containers can call arbitrary URLs through the HTTP Request tag. It is a useful primitive, and like any server-side request capability it is worth scoping to the destinations you intend.

How this audit reports it

Where a container names an endpoint, we show it — and we report coverage alongside, because configuring an endpoint and routing every tag through it are different things, and partial migrations are common.

The endpoint can also be set in the page rather than the container, so a container that names none has not shown that none exists. That is why the report distinguishes detected, likely, not detected and not checked rather than answering yes or no.

See this on a real container. No login, no signup.

Run a container check

Related