Back to the blog

Blog

Server-side tagging for a Shopify or WooCommerce store: what fires, what does not, and what to install

Published · Updated · 5 min read

A server container does not make a store report purchases. It forwards what the browser sends, from your own hostname instead of Google's, and that is worth doing — ad blockers and Safari between them drop a meaningful share of a shop's conversions. But the purchase has to be sent first, and on the two platforms most shops run, nothing sends it out of the box. This is the article we wish we could have handed every customer on day one.

The nine events, and the one thing they have in common

GA4 defines nine events for a purchase funnel: view_item_list, view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info and purchase. Their names are GA4's, not any platform's — the same nine work for Shopify, WooCommerce and a shop you built yourself.

What they share is a mechanism. Each one is a push to the page's dataLayer, by exactly that name, carrying an ecommerce object in GA4's shape. A Tag Manager trigger listens for the name; a GA4 event tag reads the ecommerce object and sends it. Misspell the name, nest the object one level too deep, or push it on the wrong page, and the tag sits there forever without an error. This is what a purchase push looks like:

window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'ORD-10442',
    value: 89.90,
    currency: 'EUR',
    items: [
      { item_id: 'SKU-118', item_name: 'Trail jacket', price: 89.90, quantity: 1 }
    ]
  }
});

The push of ecommerce: null first is not decoration. Without it, the previous event's items can bleed into the next one, and a view_item from three pages ago shows up inside a purchase.

Shopify: the theme cannot see the checkout

A container installed in a Shopify theme loads on the storefront — product pages, collections, the cart. It does not load on the checkout or the thank-you page, which Shopify serves from its own domain and its own templates. So a theme-installed container can send view_item and add_to_cart and will never, under any configuration, send begin_checkout, add_shipping_info, add_payment_info or purchase.

Those four come from a Custom Pixel. Shopify's pixel sandbox exposes checkout events (checkout_started, checkout_shipping_info_submitted, payment_info_submitted, checkout_completed) and lets a pixel push to a dataLayer of its own — a small script maps each to the GA4 event name and shape above. The storefront events still come from the theme, so a complete setup has both: a dataLayer app or theme snippet for the browsing half, a Custom Pixel for the checkout half.

One more thing to decide. Shopify's Google & YouTube channel can send GA4 events directly, outside any container. If you connect a GA4 property there AND route the same property through your container, every purchase is counted twice. Pick one path. If the point is first-party collection, the container is the path, and the channel's Analytics connection stays off.

WooCommerce: core pushes nothing

WooCommerce itself writes no dataLayer at all. A plugin does — GTM4WP is the one most people reach for — and its GA4 e-commerce option produces these events in the right shape on the right pages: view_item on a product, add_to_cart on the button, begin_checkout on the checkout page, purchase on the order-received page.

The classic WooCommerce fault is a doubled purchase, and it has a specific cause: the order-received page reloads. A visitor refreshes it, returns to it from a payment provider, or the theme redirects through it twice, and the purchase pushes again with the same transaction_id. GA4 deduplicates purchases by transaction_id within a session, which catches most of it, but not a reload the next day. The plugin's "only fire once per order" setting is worth turning on; it keeps the flag server-side, where a reload cannot reset it.

What SignalHost builds for you

Every SignalHost container has, in its web container, a Custom Event trigger and a GA4 event tag for each of the nine — and for the seven a site without a shop needs as well: sign-ups, trials and leads. They are all named with an SH prefix, so they never collide with anything you already have. Each tag sends the ecommerce object straight from the dataLayer, so items, value, currency and transaction_id come through without a variable per field, and each names your measurement ID explicitly rather than inheriting it.

Nothing is installed on your store. The triggers listen; the pushes are the plugin's or the pixel's. If your container already sends one of these events to GA4 with a tag of your own — a plugin that brought its own purchase tag, say — SignalHost leaves that event to it and builds no second tag, so no order is counted twice. Each tag also passes on an event_id you push with the event, so an ad platform that hears the same event from its own pixel counts it once.

On the server side there is nothing per event. The server container's GA4 tag forwards every event it receives, whatever it is called, so a new event on your site needs no change on ours. The ad platforms you connect — Google Ads, Meta, TikTok, Reddit, Pinterest, Snapchat, LinkedIn — hear the same events: a purchase becomes a Purchase for Meta and TikTok, with its products and value, and each platform receives the events it has a use for, from the same push.

Checking it with one test order

Place a real order in a test mode, or a cheap one you refund, and watch the container page's event list. In sequence you should see view_item, add_to_cart, begin_checkout, add_payment_info and purchase — each once, with view_cart and add_shipping_info between them if your setup pushes those. A missing purchase on Shopify means the Custom Pixel is not installed or not pushing; a missing begin_checkout on WooCommerce usually means the plugin's checkout event is disabled.

If you run Google Ads conversions through the container, purchase is the conversion event unless you choose another, and its value, currency and transaction ID are read from that same ecommerce object. A purchase push with no value records a conversion worth nothing — which is a real number in a report, and the wrong one.

If GA4 still counts fewer orders than your shop after that, here is why GA4 undercounts, and what can be won back.

Stop losing data to ad blockers

Start free. Get your first container running in minutes.