25% off Server GTM for 12 months

Claim 25% off

Shopify Server-Side Tracking with GTM: 2026 Setup Guide

November 11, 2026 8 min read

The short answer: On Shopify, server-side tracking means loading your web Google Tag Manager container in a custom pixel (Settings → Customer events), pushing Shopify's standard events such as checkout_completed into the dataLayer, and setting server_container_url on the Google tag so the events go to your server GTM container on your own subdomain. The server container then sends them on to GA4, Meta, Google Ads and TikTok. Shopify runs custom pixels in a sandbox, so cookies need extra care, and Google Tag Assistant cannot see the tags: test with Shopify Pixel Helper and server-side Preview instead.

This guide follows Shopify's own Google Tag Manager tutorial and Web Pixels API reference, plus our own server-side GTM hosting. It applies to every Shopify plan, because custom pixels run on the checkout and thank-you pages for Plus and non-Plus stores alike.

How Shopify tracking works now

Shopify no longer lets you paste scripts into the checkout. Checkout and thank-you pages run on Checkout Extensibility, and tracking there goes through Customer events: app pixels (from apps like Google & YouTube or Meta) and custom pixels that you write yourself. A custom pixel runs in a sandbox (an iframe), sees Shopify's standard events through analytics.subscribe(), and can load Google Tag Manager.

That gives you one place that sees the whole journey, from page_viewed to checkout_completed. From there, server-side GTM is the same as on any site: the web container sends events to your server container, and the server container sends them to the ad platforms.

What you need

  • A server GTM container running on a host. On Taglinc the Free plan covers 15,000 requests a month; a small store usually needs Pro (500,000). See Deploy your first server GTM container.
  • A tracking subdomain of your store, such as data.yourstore.com. See Custom first-party tracking domains.
  • A web GTM container (GTM-XXXXXXX) used only for Shopify.
  • Staff permission in Shopify to manage Customer events.

Step 1: Create the custom pixel in Shopify

  1. In Shopify admin, open Settings → Customer events and choose Add custom pixel. Name it, for example, "GTM server-side".
  2. Set the customer privacy permission to match your consent setup (for most stores: required for marketing and analytics).
  3. Paste the code below, change GTM-XXXXXXX to your web container ID, then Save and Connect.
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }

// Load your web GTM container inside the pixel sandbox.
(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://www.googletagmanager.com/gtm.js?id=" + i + dl;
  f.parentNode.insertBefore(j, f);
})(window, document, "script", "dataLayer", "GTM-XXXXXXX");

const money = (m) => (m ? Number(m.amount) : undefined);
const push = (data) => { dataLayer.push({ ecommerce: null }); dataLayer.push(data); };

analytics.subscribe("page_viewed", (event) => {
  dataLayer.push({
    event: "page_viewed",
    page_location: event.context.window.location.href,
    page_title: event.context.document.title,
  });
});

analytics.subscribe("product_viewed", (event) => {
  const v = event.data.productVariant;
  push({
    event: "view_item",
    ecommerce: {
      currency: v.price.currencyCode,
      value: money(v.price),
      items: [{ item_id: v.product.id, item_name: v.product.title, item_variant: v.title, price: money(v.price), quantity: 1 }],
    },
  });
});

analytics.subscribe("product_added_to_cart", (event) => {
  const line = event.data.cartLine;
  push({
    event: "add_to_cart",
    ecommerce: {
      currency: line.cost.totalAmount.currencyCode,
      value: money(line.cost.totalAmount),
      items: [{ item_id: line.merchandise.product.id, item_name: line.merchandise.product.title, price: money(line.merchandise.price), quantity: line.quantity }],
    },
  });
});

const items = (checkout) => checkout.lineItems.map((li) => ({
  item_id: li.variant?.product?.id, item_name: li.title, item_variant: li.variant?.title,
  price: money(li.variant?.price), quantity: li.quantity,
}));

analytics.subscribe("checkout_started", (event) => {
  const c = event.data.checkout;
  push({ event: "begin_checkout", ecommerce: { currency: c.currencyCode, value: money(c.totalPrice), items: items(c) } });
});

analytics.subscribe("checkout_completed", (event) => {
  const c = event.data.checkout;
  push({
    event: "purchase",
    ecommerce: {
      transaction_id: c.order?.id,
      currency: c.currencyCode,
      value: money(c.totalPrice),
      tax: money(c.totalTax),
      shipping: money(c.shippingLine?.price),
      items: items(c),
    },
    user_data: { email: c.email, phone: c.phone },
  });
});

The event names here are already GA4's (view_item, add_to_cart, begin_checkout, purchase), so GA4's ecommerce reports fill in without renaming. Shopify's full list of standard events also includes collection_viewed, search_submitted, cart_viewed and payment_info_submitted; add them the same way if you need them.

Customer data: the user_data on purchase is there for Meta Conversions API and Google Ads enhanced conversions, which hash it in the server container. Never send it to GA4: Google Analytics does not allow personal data such as email addresses.

Step 2: Set up the web GTM container

  1. Google tag: add your GA4 measurement ID. Under configuration settings add server_container_url = https://data.yourstore.com and send_page_view = false (the pixel sends page views itself, with the real page address instead of the sandbox address).
  2. Triggers: Custom Event triggers named exactly page_viewed, view_item, add_to_cart, begin_checkout and purchase.
  3. GA4 event tags: one per trigger. For page_viewed send a page_view event with page_location from a dataLayer variable. For the ecommerce events, tick "Send ecommerce data" from the dataLayer.
  4. Publish the web container.

All of these hits now go to your server container instead of straight to Google.

Step 3: Set up the server GTM container

  • The GA4 client is there by default and claims the hits.
  • Add a GA4 tag (trigger: all events from the GA4 client) to forward them to Google Analytics.
  • Add the Meta Conversions API tag for purchase and the other events, with the same event ID the browser pixel uses if you also keep Meta's browser pixel. See Meta Conversions API with server-side GTM.
  • Add Google Ads conversion and TikTok Events API tags as needed.

Shopify's sandbox changes how cookies behave on checkout pages. As Simo Ahava documented, requests from the checkout sandbox to your server do not carry cookies, and cookies set in the server's response are not stored. In practice:

  • GA4: hits carry the client ID from the browser, so GA4 itself works. If your GA4 tag in the server container uses the "Server Managed" client ID (FPID), switch it to JavaScript Managed, because the FPID cookie cannot be set here.
  • Google Ads and Meta click IDs (gclid in _gcl_aw, fbclid in _fbc): the server container cannot read these cookies on checkout. Read them in the pixel with browser.cookie.get() and send them as event parameters, then map them to the click ID fields of your server tags.
  • Cookie Keeper and other server-set cookies do not work on Shopify's checkout pages for the same reason. They still work on your theme pages if you also load GTM there.

Step 5: Test it

  1. Install Shopify Pixel Helper (a Chrome extension) and walk through product, cart, checkout and a test order. Every event should show without errors.
  2. Open Preview in your server GTM container. Google Tag Assistant does not detect tags inside a custom pixel, so the browser side of Preview stays empty. To see the requests in server Preview anyway, switch on the Preview Header Power-Up on Taglinc while you test, then switch it off.
  3. Check GA4 DebugView and Meta Events Manager → Test events for the purchase with the right value and currency.
  4. On Taglinc, the container's Event Logs show each request as it arrives (paid plans).

App pixel or custom pixel?

Shopify's Google & YouTube app sends GA4 and Google Ads data from Shopify's own pixel, and Meta's app can send Conversions API events from Shopify's servers. They are quicker to set up but you cannot route them through your own server container, add other platforms in one place, or control the data. Use the custom pixel above when you want one server container in charge. Do not run both for the same platform, or you will count conversions twice.

When it is not worth it

If your store gets a few hundred orders a month and you only advertise on Meta, Meta's own Shopify app with its built-in Conversions API may be enough. Server-side GTM pays off when you run ads on several platforms, want first-party data on your own domain, or need to fix missing conversions across GA4, Google Ads and Meta.

FAQ

Does this work without Shopify Plus?

Yes. Custom pixels run on checkout and thank-you pages on every plan.

Can I put GTM in theme.liquid instead?

Only for theme pages (home, collection, product, cart). It does not run on checkout. You can do both, but then fire each event from one place only.

How many requests will my store use?

Roughly your monthly GA4 event count. A store with 20,000 sessions a month sends around 300,000 requests, which fits Taglinc Pro. See Monthly request limits and quota.

Why do my GA4 page URLs show a strange sandbox address?

Automatic page views inside the sandbox use the iframe's address. Turn off send_page_view and send page_viewed from the pixel with event.context.window.location.href, as in step 1.

Sources

Get new articles in your inbox

New guides on tracking and analytics, once or twice a month. No spam, unsubscribe any time.