Server-side GTM (sGTM) setup
Host your Google Tag Manager server container with us: create it, connect your domain, configure GTM, install the loader, send GA4 and Meta Conversions API events without double counting.
TrackPilot runs Google's official tagging-server image for you. You keep building tags in Google Tag Manager as usual; we host the server container, give it a first-party URL and count its requests. Setup takes about 20 minutes.
Starting from an empty GTM? See GTM setup from scratch for the data layer, every variable, trigger and tag, and the server container.
Before you start
- A Google Tag Manager account with a web container (ID like
GTM-ABC1234) already installed on your site. - A TrackPilot account. The number of sGTM containers you can run depends on your plan (see Billing).
- Access to your domain's DNS, to add one CNAME record.
Step 1 — Create a server container in Google Tag Manager
- Open tagmanager.google.com → Admin → + next to Container.
- Give it a name (for example
server-yourshop.com) and pick Server as the target platform. - Choose Manually provision tagging server and copy the Container Config — a long string that starts like
aWQ9R1RN….
Already have a server container (for example on another host)? Open it → Admin → Container Settings and copy the same Container Config. Your tags, triggers and clients come with it.
Step 2 — Create the container in TrackPilot
- Dashboard → sGTM → New container.
- Enter a name, paste the Container Config and click Create container.
- The status moves requested → deploying → running within 1–2 minutes. Reload the page to see it.
The container page shows the sGTM container ID, the built-in URL and the requests used this month. You can stop, start or delete the container there at any time.
Step 3 — Connect your own domain
A subdomain of your site makes gtm.js and every hit first-party: longer-lived cookies and far fewer ad-blocker losses.
- Container page → Domains → add a hostname such as
data.yourshop.com. Pick a neutral word: avoidgtm,analytics,tracking,pixelortag, which ad blockers look for. - At your DNS provider create the CNAME record shown in the dashboard. If your DNS is on Cloudflare, set it to DNS only (grey cloud).
- Wait until both hostname and SSL show active (usually minutes; the page refreshes by itself).
Below we write https://data.yourshop.com — use your own hostname everywhere it appears.
Step 4 — Configure the server container in GTM
- Server container URL: server container → Admin → Container Settings → add
https://data.yourshop.com→ Save. - Web Container client (required): Clients → New → Google Tag Manager: Web Container → Allowed Container IDs = your web container ID (
GTM-ABC1234) → Save. Without this client the tagging server answersgtm.jsandns.htmlwith 400 Bad Request. - GA4 client: new server containers already have one. Keep it — it receives the events your website sends.
- Submit → Publish. The tagging server picks up a new version within about a minute.
Check: https://data.yourshop.com/gtm.js?id=GTM-ABC1234 should return JavaScript, and https://data.yourshop.com/healthz should return status 200.
Step 5 — Install the first-party loader on your website
- Container page → Install code (Custom Loader) → pick your tagging-server URL, enter your web container ID → Generate.
- Paste the first snippet as high as possible inside
<head>and the<noscript>snippet right after the opening<body>tag. - Remove the old Google Tag Manager snippet — the page must load GTM only once.
The snippet looks like this (generate yours in the dashboard):
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;
j.src='https://data.yourshop.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-ABC1234');</script>
Step 6 — Send GA4 events to the server
- Web container → your Google tag (GA4 configuration) → Configuration settings → add
server_container_url=https://data.yourshop.com. Optionally addfirst_party_collection=true. - On each GA4 event tag that carries shop data (
view_item,add_to_cart,add_payment_info,purchase): More settings → Ecommerce → tick Send Ecommerce data, data source Data Layer. This sends theitemslist to the server. - Also add these event parameters, so the server can build complete Meta events:
| Parameter | Value |
|---|---|
event_id | {{Event ID}} — the same variable the Meta Pixel tags use (Step 8) |
value | order or item value (number) |
currency | for example BDT |
content_ids, content_type, content_name, num_items | from your data layer |
contents | {{JS - FB Contents (String)}} — must be a JSON string, see below |
GA4 sends every parameter as text, so an array arrives as [object Object]. Create two Custom JavaScript variables (they read the Data Layer Variable ecommerce.items):
JS - FB Contents (String)
function () {
var items = {{DLV - ecommerce.items}} || [];
return JSON.stringify(items.map(function (i) {
return { id: String(i.item_id), quantity: Number(i.quantity) || 1, item_price: Number(i.price) || 0 };
}));
}
JS - FB Value (use it for value when your data layer has no order total)
function () {
var items = {{DLV - ecommerce.items}} || [];
var total = 0;
for (var k = 0; k < items.length; k++) {
total += (Number(items[k].price) || 0) * (Number(items[k].quantity) || 1);
}
return total;
}
For purchase, prefer the real order total (with shipping and discounts) when your data layer has it.
Step 7 — Meta Conversions API in the server container
- Server container → Templates → Search Gallery → add Conversions API Tag (by facebookincubator).
- Tags → New → Conversions API Tag:
- Pixel ID — your Meta Pixel / dataset ID.
- API Access Token — Events Manager → your dataset → Settings → Generate access token.
- Action Source = Website, tick Extend Meta Pixel cookies (fbp/fbc) and Enable Event Enhancement.
- Test Event Code — only while testing (copy it from Events Manager → Test events). Remove it before going live, or Meta treats every server event as a test.
- Trigger: a Custom trigger that fires on all events coming from the GA4 client (for example Client Name equals GA4).
- Submit → Publish.
Step 8 — Deduplication: one Event ID for browser and server
The browser Pixel and the server send the same event. Meta keeps one only when both carry the same event ID.
- Create one Event ID variable in the web container (for example the Event Id variable template from the Community Gallery).
- Use the official Meta Pixel tag template. If GTM shows an update for an older "Facebook Pixel" template, accept it — the old one has no Event ID field.
- Every Meta Pixel tag (PageView, ViewContent, AddToCart, AddPaymentInfo, Purchase) → More Settings → Event ID = {{Event ID}}.
- Every matching GA4 event tag → parameter
event_id= {{Event ID}} (Step 6).
Already sending server events yourself? The Meta Pixel tag's Meta-enabled Conversions API integration option is not needed; you can leave it off.
More on how matching works: Event deduplication.
Step 9 — Test
- GTM Preview on the server container: open your site, then check each event's Event Data —
contentsmust be a JSON list andvaluea number — and that the Conversions API tag shows an outgoing request tograph.facebook.comwith status 200. - Events Manager → Test events: each action shows a Browser row and a Server row. The server row says Deduplicated and both share one Event ID.
- Meta Pixel Helper (Chrome extension): open an event — it shows the Event ID the browser sent.
After publishing, reload the test page with Ctrl+Shift+R: browsers keep the previous gtm.js for a few minutes.
Troubleshooting
| What you see | Cause | Fix |
|---|---|---|
gtm.js returns 400 | No Web Container client, or not published yet | Step 4, publish, wait one minute |
| Domain stays pending | CNAME missing or proxied | Add the CNAME from the dashboard; on Cloudflare use DNS only |
| Container shows error | Container Config pasted incompletely, or your plan's container limit reached | Copy the config again; check Billing; contact support |
| No server rows in Test events | The CAPI tag's Test Event Code is not today's code | Copy the current code from Test events into the tag (remove it before going live) |
contents shows [object Object] | An array sent as a GA4 parameter | Use the JSON string variable from Step 6 |
Server event has only value and currency | items not sent to the server | Tick Send Ecommerce data on the GA4 event tags (Step 6) |
| Browser row has no Event ID in Test events | That column is often empty for browser rows; or the page still loads the old gtm.js | Check with Meta Pixel Helper after a hard reload |
| Events counted twice | Browser and server event IDs differ | Use the same {{Event ID}} variable on both sides (Step 8) |
Moving from another sGTM host
- Create the container in TrackPilot with the same Container Config (Step 2) — all your tags, triggers and clients stay in Google Tag Manager.
- Point your tagging domain's DNS to us (Step 3). Once it shows active, traffic reaches the new container.
- Add the Web Container client if your old host served
gtm.jsits own way (Step 4), and use our loader snippet (Step 5). - Test (Step 9), then cancel the old hosting.
Usage and limits
Every request to your tagging server counts toward the monthly allowance of your plan. The sGTM page shows requests this month / limit and how many containers your plan allows; upgrade on the Billing page.
Stuck? Ask for setup help — we answer in বাংলা or English.