AuditTags
ShopifyGA4CheckoutCheckout ExtensibilityTroubleshooting

Shopify Checkout Extensibility: Why GA4 Tracking Breaks

Migrating from checkout.liquid to checkout extensibility breaks GA4 purchase tracking for most stores. Here is what changes and what you need to do.

A
AuditTags Engineering
Shopify Analytics Specialists
10 min read
Shopify Checkout Extensibility: Why GA4 Tracking Breaks

You migrated to Shopify's new checkout. Your theme looks great, the flow feels faster, and your conversion rate ticked up. Then your GA4 purchase events stopped firing.

Not "stopped working perfectly." Stopped. Completely.

This is happening to hundreds of Shopify stores right now, and most of them don't realize it until a CFO asks why revenue in GA4 is half what Shopify reports — or worse, until ad campaigns start optimizing toward ghost conversions that haven't been tracked in weeks.

If you're migrating from checkout.liquid to checkout extensibility, or you've already migrated and something feels off, this post is your map.


Why Checkout Extensibility Breaks GA4 Tracking

The short version: checkout.liquid let you inject arbitrary JavaScript directly into checkout pages. Checkout extensibility does not.

That's it. That's the whole problem.

For years, the standard way to fire a GA4 purchase event was to drop a script tag into checkout.liquid, or use Shopify's "Additional Scripts" section in the old checkout settings. GTM containers lived there. GA4 config tags lived there. Custom dataLayer.push() calls lived there. All of it.

Checkout extensibility replaces that model entirely. The new checkout runs in a sandboxed environment where arbitrary script injection is not allowed. If your tracking relied on any of those old methods, it broke the moment you switched.

Think of it like this: the old checkout was like renting a house — you could knock down walls, rewire the electrics, paint whatever color you wanted. The new checkout is a serviced apartment. You get a clean, fast, well-maintained space, but you're working within a defined structure. You can customize a lot, but you can't rewire the building.

The good news is Shopify built proper tracking infrastructure into the new system. The bad news is you have to actually use it, and it works differently from everything you're used to.


What Actually Changed: The Three Breakage Points

1. checkout.liquid Script Injection Is Gone

If you had GA4 tracking inside checkout.liquid — whether a raw gtag.js snippet, a GTM container tag, or a manual dataLayer push — none of that runs on the new checkout. The file itself is deprecated and Shopify no longer renders it.

Same goes for "Additional Scripts" under Settings → Checkout. That field is disabled for stores on checkout extensibility.

2. GTM Doesn't Load on Checkout Pages

This is where most merchants get caught. Your GTM container fires everywhere else on the site. Product pages, collection pages, cart — all fine. Then the purchase event fires at the end of checkout, which GTM never sees.

Your GA4 account looks healthy until you filter by event name and notice purchase has gone silent.

3. Order Status Page ≠ Checkout Completion

Some stores tried to work around the checkout restriction by tracking on the order confirmation page instead. This is unreliable for a different reason: customers who pay via accelerated checkouts (Shop Pay, PayPal, Google Pay) may not land on your standard order status page at all. You catch some purchases. You miss others.


The Correct Architecture: Shopify Web Pixels

Shopify's answer to this problem is the Web Pixels API. It's a sandboxed JavaScript environment specifically designed for analytics and tracking inside the new checkout. You write a pixel, it subscribes to Shopify's native checkout events, and you fire your tracking from there.

For GA4, that means a custom pixel that listens to the checkout_completed event and fires a purchase event via gtag.

Here's what the core of that looks like:

analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;

  const items = checkout.lineItems.map((item, index) => ({
    item_id: item.variant?.sku || item.variant?.id,
    item_name: item.title,
    item_variant: item.variant?.title,
    price: parseFloat(item.variant?.price?.amount),
    quantity: item.quantity,
    index: index,
  }));

  gtag('event', 'purchase', {
    transaction_id: checkout.order?.id,
    value: parseFloat(checkout.totalPrice?.amount),
    tax: parseFloat(checkout.totalTax?.amount),
    shipping: parseFloat(checkout.shippingLine?.price?.amount || 0),
    currency: checkout.currencyCode,
    items: items,
  });
});

You register this pixel either through the Shopify Admin (Settings → Customer Events) or via the Shopify CLI if you're building an app.

Customer Events vs. App Pixels

There are two ways to deploy a Web Pixel:

Custom pixels (via Settings → Customer Events) are store-specific, managed directly in the admin, and don't require an app. They're the right choice for most merchants implementing their own GA4 tracking.

App pixels are registered by Shopify apps through the API. If you're using a third-party analytics app, it should be handling this for you — but verify that it actually is. Just because an app "supports GA4" doesn't mean it's been updated for checkout extensibility.


Handling GTM in the New Checkout

GTM's standard web container cannot be loaded in the checkout extensibility sandbox. Full stop.

However, there are two legitimate paths forward:

Option 1: Migrate Purchase Tracking to a Native Pixel

This is the cleanest approach. Move your purchase event tracking out of GTM entirely and into a custom Web Pixel. Keep GTM for everything else on the storefront.

Your funnel tracking now has two systems: GTM for the storefront, native pixels for checkout. That sounds messy but it's actually clean in practice — the boundary is clear.

Option 2: Use GTM's Server-Side Container

If you need GTM for checkout (for complex tag logic, consent handling, or you're just deeply committed to the GTM workflow), the path forward is a server-side GTM container. You send data from the Web Pixel to your sGTM endpoint, and let sGTM forward it to GA4.

This adds infrastructure overhead, but it keeps your tag management centralized. For larger stores with complex tracking requirements, it's often worth it.

// In your Web Pixel, instead of firing gtag directly,
// send to your sGTM endpoint
analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;

  fetch('https://your-sgtm-endpoint.com/collect', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      event_name: 'purchase',
      transaction_id: checkout.order?.id,
      value: parseFloat(checkout.totalPrice?.amount),
      currency: checkout.currencyCode,
      // ... additional params
    }),
  });
});

Note that fetch availability inside Web Pixels depends on Shopify's sandbox permissions. Always test this in a development store before going live.


Here's where it gets genuinely complicated. If you're using a Consent Management Platform (OneTrust, Cookiebot, Axeptio, etc.), your tracking is probably conditional on user consent. The old flow was: CMP fires on page load, sets consent state, GTM respects it. That whole chain assumed GTM was present.

In the checkout sandbox, your CMP almost certainly doesn't run.

There are a few approaches depending on your setup:

If you're using Shopify's built-in privacy API: You can check analytics.customerPrivacy.currentVisitorConsent() inside your pixel before firing. Shopify's own consent UI integrates with this directly.

If you're using a third-party CMP: You'll need to either pass consent state into the pixel via metafields/session storage (complex), or accept that checkout events fire without granular consent signals and rely on Shopify's platform-level consent controls.

Neither is perfect. This is an area where the ecosystem is still maturing. The important thing is to document your approach and make sure your legal team understands what's actually happening — not what you assumed was happening when you were on checkout.liquid.


Revenue Discrepancy Checks After Migration

Even after you've implemented Web Pixels correctly, there's a validation step most stores skip: confirming that the numbers actually match.

GA4 purchase revenue should be within a small margin of Shopify's reported revenue. "Small margin" means accounting for things like returns, test orders, and the fact that GA4 uses event time while Shopify uses order creation time. A 2-3% difference is normal. A 15% difference is a signal.

Common sources of post-migration discrepancy:

  • Double-firing: Old tracking code wasn't fully removed and the pixel fires alongside a legacy snippet
  • Missing accelerated checkout orders: Shop Pay or PayPal completions not captured
  • Currency mismatches: totalPrice.amount is in the presentment currency, not necessarily the store's base currency — make sure GA4 and Shopify agree on which currency you're reporting
  • Checkout session issues: Users who start checkout, leave, and complete later may trigger the pixel in unexpected states

If you're seeing GA4 revenue running higher than Shopify reports, that's often a double-fire situation. We wrote about this pattern in detail in our post on GA4 revenue discrepancies vs. Shopify.


The Migration Checklist

Before you declare your tracking fixed, run through this:

Remove the old tracking:

  • Confirm "Additional Scripts" field is empty or disabled
  • Confirm no GA4/GTM snippets remain in your theme's checkout.liquid (if it still exists)
  • Check for any Shopify apps that may be injecting checkout scripts the old way

Verify the new setup:

  • Custom pixel (or app pixel) is active in Settings → Customer Events
  • Pixel subscribes to checkout_completed and checkout_started at minimum
  • purchase event includes all required GA4 ecommerce parameters
  • Test orders fire correctly in GA4 DebugView

Validate the data:

  • Compare order counts in GA4 vs. Shopify for the past 30 days
  • Check revenue totals align within expected margin
  • Verify Shop Pay / accelerated checkout orders are captured
  • Confirm transaction_id de-duplication is working (same order doesn't fire twice)

For the ecommerce event structure standards your GA4 implementation should follow, this breakdown of Shopify GA4 ecommerce events covers the full spec.

And if you went through this migration and things still look wrong, it might not be checkout extensibility at all — theme updates can also silently break tracking in other parts of the funnel. That pattern is covered in what to check when Shopify tracking breaks after a theme update.


Automated Validation with AuditTags

The manual checklist above is a good start, but it has an obvious weakness: you only run it when you think something is wrong. Most tracking breaks silently and stays broken until someone asks an uncomfortable question in a reporting meeting.

AuditTags monitors your Shopify store's tracking setup continuously and flags exactly the failure patterns covered in this post.

For checkout extensibility specifically, AuditTags detects:

  • Missing purchase events: If your store is processing orders but purchase events aren't reaching GA4, your store's health score moves to Critical — not as a warning, but as a confirmed absence. This is what AuditTags calls a NO_TRACKING signal: we're not guessing, we've verified that tracking that should be present isn't there.

  • Checkout/storefront tracking mismatches: AuditTags compares event coverage across your storefront and checkout flow, surfacing gaps where one environment is instrumented and the other isn't.

  • Double-firing detection: If your migration was incomplete and both a legacy snippet and a Web Pixel are firing the same event, AuditTags catches the duplication before it corrupts your attribution data.

  • Revenue integrity checks: By comparing GA4 purchase data against Shopify order data, AuditTags surfaces discrepancies that indicate tracking holes — without you having to build the comparison manually in a spreadsheet.

When AuditTags runs in LITE_MODE (a partial scan based on publicly observable signals), some of these checks have limitations — particularly around sandboxed checkout events that can't be observed from the outside. A full scan, with the AuditTags Shopify app installed, can validate checkout pixel activity directly.

The point isn't to replace your judgment. It's to make sure you know when something breaks in the first hour, not the third week.

If you migrated to checkout extensibility in the last six months and haven't validated your GA4 purchase tracking since, run an audit. The setup feels right until the numbers don't add up — and by then, you've already made decisions based on bad data.


Finished the Setup? Verify It Works.

After implementing the steps above, run a scan to confirm your GA4/GTM configuration fires correctly.

Validate My Implementation

Or What the scan checks to see what you'll get.