Server-Side GTM: What It Is, How to Configure It, and When It Beats the Web Container
Almost every sGTM pitch makes the same promise. Move your tags to a server, and your measurement problems go away.
Part of that is true. The part that is not true is the expensive part, and it is the part people buy.
I have inherited a lot of server containers built by somebody else. The pattern repeats. The container is live, the bill arrives every month, and the setup is still losing the same events it was losing before, because the two configuration decisions that carry most of the value were skipped during the build.
So this is what sGTM is, what it changes, the exact order to build it in, and the point at which it stops being worth the money.
Server-side Google Tag Manager is a second GTM container that runs on a server you pay for instead of in the visitor's browser. The browser still collects the event and posts it to your container. The container decides what each vendor is allowed to see, then forwards it.
The two decisions that determine whether it was worth building: whether your tagging endpoint runs in a genuine first-party context, and whether you configured transformations before you connected any tags. Get either wrong and you have bought a proxy with a monthly bill.
What this covers
What sGTM is, in parts
Google describes server-side tagging as moving measurement logic out of the browser into a container you run. Accurate, and vague enough to be useless when you are staring at the interface for the first time. Here is what is inside it.
A client. This is the piece that claims an incoming HTTP request. The GA4 client recognises a request shaped like a GA4 hit, parses it, and turns it into an event data object. Nothing else in the container runs until a client has claimed the request.
Transformations. These sit between the client and your tags, and they are the reason to run sGTM at all for anyone with a compliance obligation. More below.
Tags. These read the event data object and forward it outward. GA4, Google Ads, Floodlight, Meta Conversions API, TikTok, whatever else you feed.
What is not inside it: your web container. sGTM does not replace GTM. It sits behind it. You keep the web container, the data layer, and every trigger you already wrote. Anyone describing sGTM as a migration away from client-side GTM has not built one.
In a standard sGTM setup, the first hop is still the browser. The visitor's browser still has to successfully send a request to your endpoint before your server ever sees the event. Everything that kills that request in the browser, blockers, script errors, tab closures during a payment redirect, still kills it. sGTM moves the vendor-facing half of the pipeline out of harm's way and leaves the collection half exposed.
That is why the highest-value events, paid orders and refunds, should come from your backend rather than from a page view. See our note on server-side tagging versus server-side tracking for where the line sits.
The three things it protects
1. The payload
Without a server container, whatever your data layer pushes goes to the vendor. All of it. Every parameter, every stray query string, every field a developer added six months ago for a different reason.
Transformations end that. Google's documentation covers three rule types: allow only explicitly defined parameters through, augment parameters by editing or adding them, and exclude parameters so tags never see them. Rules can be scoped to all tags, a tag type, or one specific tag, and they can be conditional.
The practical version. An email address ends up in a URL query parameter on a password reset flow. Without transformations, that string travels to Google Ads and Meta inside page_location. With an exclude rule, it does not leave your server. That is a data protection control you can point at during a DPIA, not a marketing claim.
One caution, because this bites people. The allow rule discards everything not explicitly listed, including parameters Google's own tags require. Inventory what your tags consume before you write it, or you will break measurement in a way that looks like a tracking bug for a week.
Order matters too. The default run order is allow, then augment, then exclude, and you override it with a priority value where higher wins.
2. The identifier
This is where most builds leak value.
Set the GA4 client to server-managed cookies and it writes an FPID cookie in the HTTP response, flagged HttpOnly. Safari's seven-day cap applies to cookies written in JavaScript, so an HTTP-set cookie escapes it. That is the actual mechanism behind the cookie lifetime argument for sGTM, and it is worth having straight, because it only works under one condition.
Google is unambiguous about that condition. Their custom domain documentation lays out three hosting options: same origin, which they call best practice and which means serving the tagging server from a path on your main domain through a CDN or load balancer; a subdomain; or the default cloud provider domain. On the default domain you get no server-set cookie benefits at all. You can only set JavaScript cookies, which puts you exactly where you started, except paying for hosting.
And there is a second trap on top of that one. WebKit detects third-party CNAME cloaking and third-party IP address cloaking and caps cookies set in the HTTP response to seven days when it sees either. So a tagging subdomain that resolves into a vendor's IP range rather than your own gets capped anyway. Run dig against your tagging hostname and compare it against your site origin. I find this wrong more often than I find it right.
3. The destination list
One event arrives. You decide how many places it goes and in what shape. Meta gets the fields Meta needs, Ads gets the fields Ads needs, and neither one gets a view of your data layer.
That also means new platform integrations stop being a developer ticket. Adding a channel becomes a tag in a container rather than another vendor script on the page, which is the load-time argument for sGTM and the only one I find consistently holds up.
Compare it against what you already run
Four configurations, ordered by what they cost you rather than by how they get sold. Sort by any column. On mobile each row becomes a card.
| Web GTM only | None | Whatever the data layer pushes | Already running | Zero |
| Google tag gateway | Partial | None, hostname changes only | An afternoon | CDN cost only |
| sGTM on a subdomain | Full, if DNS resolves to you | Full transformations | Two to four days | Around 90 USD on Cloud Run |
| sGTM same origin | Full | Full transformations | A week, plus infra sign-off | Cloud Run plus load balancer |
If the only thing you want is Google and Meta requests coming from your own hostname, Google tag gateway does that for a fraction of the effort. Read the row above before you sign off on a container build.
Configuration, in build order
The order below is deliberate. Steps four and seven are the ones that get postponed on rushed builds, and both are painful to retrofit once traffic is flowing.
-
Create the server container
In GTM, create a new container and choose Server as the container type. You get a container configuration string. Keep it, you will need it when you provision manually or move regions later.
-
Provision the tagging server, then immediately resize it
Automatic provisioning gets you running fast, and it gets you a testing configuration: a Cloud Run service that scales to zero when idle. Cold starts drop events. Do not leave production on it.
Google's Cloud Run setup guide recommends a minimum of two instances to reduce data loss risk during an outage, and states that autoscaling from two to ten servers handles roughly 35 to 350 requests per second depending on how many tags run and what they do. Set CPU to always allocated, 1 vCPU, 0.5 GB. Set a billing alert at the same time.
-
Leave the preview server alone
The preview service is separate and should stay at minimum zero, maximum one instance. Scaling it past one breaks preview mode, which you will need every single time you debug something.
-
Configure the custom domain, and verify the DNS
Same origin if your infrastructure team will do it, subdomain otherwise, default domain never. Then confirm the hostname resolves into your own IP range, not your host's. This single check is the difference between two-year cookies and seven-day cookies in Safari, and no amount of container configuration compensates for getting it wrong.
dig +short metrics.example.com dig +short www.example.com -
Configure the GA4 client
Set cookies and client identification to server-managed so the client writes FPID rather than reading the JavaScript _ga cookie. If you are moving an established property, enable the migration option so existing visitors are not counted as new users on day one.
For cross-domain, the client also writes FPLC, a non-HttpOnly hash of FPID that JavaScript can read for link decoration. Both domains need their own server container, and those containers need to sit in the same GTM account.
-
Point the web tag at the container
In GTM, add server_container_url as a configuration setting on the Google tag. In raw gtag.js it looks like this.
gtag('config', 'G-XXXXXXXXXX', { 'server_container_url': 'https://metrics.example.com' });Deploy to a fraction of traffic first if your platform allows it. Watch realtime for an hour before you push it to everyone.
-
Write transformations before you connect a single tag
Do this in the gap between the client working and the tags going live. It is a two-hour job at that point. Once four vendors are receiving events, auditing what each one gets is a project.
Start with an exclude rule for anything that can carry personal data through a URL, then decide per tag type what is genuinely needed. Verify in preview: open a fired tag, check the Tag Details view for which transformations ran and in what order, and use Show Original to compare the event data before and after.
-
Add Meta CAPI with deduplication from the first deploy
Browser and server copies of the same purchase need a shared event_id. Meta's documentation is explicit that matching event name plus matching event ID is how the two are recognised as one action. Pass fbp and fbc, set action_source correctly, and watch Event Match Quality rather than raw event volume. Volume rises either way and only one of those outcomes is good.
-
Log to BigQuery, and keep the log narrow
A custom tag using the BigQuery API gives you your own copy of every event, which is what you will reconcile against orders later. Pair it with an allow transformation scoped to that tag so you are not storing fields you have no basis to keep. It reduces storage cost and it is the right answer when someone asks what is in the warehouse.
What it costs, without the sales gloss
Google publishes the number. Each server costs approximately 45 USD per month at 1 vCPU and 0.5 GB with CPU always allocated. That figure gets quoted constantly, and on its own it is misleading, because Google recommends running two.
Per instance
45 USDGoogle's own figure, for one always-allocated instance at 1 vCPU and 0.5 GB. Excludes load balancing and egress.
Recommended minimum
2Google recommends two instances to reduce the risk of data loss during a server outage. That is the honest floor, not one.
Capacity band
35 to 350Requests per second handled by two to ten autoscaling servers, varying with how many tags run and what those tags do.
Managed hosts price differently, by incoming requests to your container rather than by sessions or events. One page view firing GA4, Ads and Meta CAPI is one incoming request, not three. That distinction changes the comparison significantly and most pricing pages do not spell it out.
The number nobody puts on a pricing page is engineering time. Image updates, monitoring, scaling, debugging when a tag starts timing out at 3am on Black Friday. If you have GCP capability in house, self-hosting is cheaper. If you do not, the sticker price is not the price.
Check whether your quota model degrades or deletes. Some managed plans pause the container once you exceed your allowance, which means requests are dropped rather than throttled. Cheap plan, seasonal spike, and your peak trading week is gone. Read the overage terms before the plan tier.
A Voxxy implementation
The clearest one we can publish is an agency engagement. Their ecommerce client was losing roughly 25 percent of purchase events to ad blockers. Not delayed, not modeled. Absent.
Nobody had gone looking for that. It surfaced the way it usually does, when someone asked the agency to defend a ROAS figure that would not reconcile against the client's own order data, and the honest answer was that it could not be defended either way.
What we built: server-side GTM with the tagging endpoint running in a genuine first-party context, and Meta Conversions API alongside the existing pixel, with shared event IDs so the browser and server copies of a purchase were recognised as one action rather than two.
The sequencing was the same as the build order above, and both decisions were deliberate. The endpoint and its DNS were settled before anything else, because a container sitting behind a hostname that resolves into somebody else's IP range hands back the cookie problem you just paid to fix. Deduplication went in with the first CAPI deploy rather than after it, because retrofitting event IDs onto a live pixel means running on knowingly inflated numbers for however long the fix takes, and those numbers get used to make bidding decisions in the meantime.
Attributed conversions
+22%Recovered in Meta within 30 days of the server-side rebuild going live.
Reported ROAS
3.0 to 3.8Same campaigns, same spend. The performance was already there. The measurement was not.
Budget decision
+15%The client raised spend on the back of numbers they were finally willing to act on. That is the actual outcome.
Full write-up in our case studies.
Now the honest reading of that. A 25 percent loss is a bad starting point, and the size of the recovery is a direct function of how bad the starting point was. We report over 30 percent average conversion data recovery across our server-side work, and that average comes from a self-selecting client base, because businesses come to us once something is already broken. A store with clean tracking recovers a fraction of that, and it should.
Which is the argument for running the reconciliation before you commission anything. Any agency putting a recovery percentage in front of you before they have looked at your data is selling, not measuring.
What it will not fix
- Consent. You still need a legal basis and a banner that works. Server-side changes where data goes, not whether you were allowed to collect it.
- Ad blockers, entirely. A first-party endpoint reduces the surface. Filter lists get updated. Anyone promising immunity is selling.
- Browser dependency, wherever the event still starts in the browser.
- A vague data layer. That needs redesigning, not relocating.
- Four platforms reporting four different numbers. Different attribution windows and different identity graphs produce different answers, and no infrastructure reconciles that.
- Duplicate events. Moving them to a server container relocates the confusion somewhere with a monthly bill.
Pre-flight checklist
Run these before you commission a build. Most of them take minutes and any one of them can change the decision.
Eight checks
0 of 8 doneQuestions we get asked
Does sGTM replace my existing GTM container?
No. The web container stays and keeps doing what it does. The server container sits behind it and receives what the web container sends. Any proposal that describes decommissioning your web container is describing something other than server-side tagging.
Does server-side tagging bypass ad blockers?
It reduces loss from common blockers. It does not eliminate it. The request still originates in the browser, filter lists are maintained continuously, and a first-party endpoint is a smaller target rather than an invisible one. Treat any vendor claiming otherwise as a signal about the rest of their claims.
Do I need a custom domain, or can I run on the default one?
You need one. On the cloud provider's default domain Google states you get no server-set cookie access at all and can only set JavaScript cookies. That removes the main technical advantage while keeping the entire monthly cost.
Cloud Run or a managed host?
If you have GCP capability in house and your legal position requires you to hold the infrastructure, Cloud Run. If you do not, a managed host is usually cheaper once you price engineering time honestly, and it takes an afternoon rather than a week. Check the overage behaviour on whichever plan you pick.
Will sGTM make my consent setup compliant?
No, and this gets claimed often enough to be worth stating flatly. Consent is a legal basis question answered before collection. What sGTM adds is enforcement: with the consent state in the event data object, you can route, redact, or drop per destination rather than trusting each vendor's own gate. More in our note on consent-aware tracking.
How long does a proper build take?
Two to four days for a subdomain setup on a store with a clean data layer, longer if same-origin serving needs infrastructure sign-off, and considerably longer if the data layer needs work first. Most of the timeline risk sits in the data layer, not the container.
If you want a second pair of eyes on the reconciliation before you commit to a build, or you have a container already running and suspect it is not doing what you were told it does, book a call with us. We will tell you if the answer is that you do not need one.
Sources
- Google for Developers: introduction to server-side tagging
- Google for Developers: set up server-side tagging with Cloud Run
- Google for Developers: custom domain configuration
- Google for Developers: control event parameters with transformations
- Google for Developers: Google tag gateway for advertisers
- WebKit: Tracking Prevention
- Meta for Developers: event deduplication
Related Posts
Why Hire a Conversion Tracking Agency?
Hiring a conversion tracking agency means accurate data, better campaign…


