LAB LIVE
SCANNING423 CAMPAIGNS
ANOMALIES15 FLAGGED
AVG LIFT+4.10× ROAS
Analytics

Enhanced Conversions Alone Won't Save Your Woo/Shopify Attribution

Enhanced Conversions for Web is the single most oversold feature in Google Ads. It recovers about 15% of lost conversions on average — real, but nowhere near the 30–50% many teams expect. To actually close the gap on WooCommerce and Shopify, you need four layers. Here's each one and why it can't stand alone.

Alex Sterling··11 min read

Every e-commerce client we take on shows the same pattern in week one: WooCommerce reports 340 orders for the month, Google Ads reports 190, GA4 reports 220. Three sources of truth, none of them right. The knee-jerk fix everyone reaches for is Enhanced Conversions for Web. Turn it on, enable auto-collection, done.

Enhanced Conversions for Web helps. It does not fix the gap. The reason is that each layer of conversion tracking fails in a different part of the funnel, and only a stack that covers all four gaps produces reliable attribution.

Where Each Layer Actually Loses Conversions

LayerLoses conversions whenTypical loss
gtag conversion snippetAd-blocker, iOS ITP, consent denied, JS error15–35%
Enhanced Conversions for WebPayment page loads on a third-party domain, or user checked out as guest with no email match10–25%
GA4 Measurement ProtocolServer never runs (front-end order, no webhook), or session context is missingRecovers what gtag lost — if wired
Enhanced Offline Conversions (EOC)gclid stored on order + upload runs — closes what everything else missesRecovers up to 90% of remaining gap

The mistake most teams make is treating these as alternatives — “we use EOC instead of gtag” or “we use MP instead of EOC.” They're not alternatives. Each one plugs a different hole. Deploying all four is how you get from 65% attribution to 92%.

Layer 1: The gtag Conversion Snippet on Thank-You

This is the baseline. When a purchase completes and lands on the thank-you page, fire a gtag('event', 'conversion', {...}) with the order value, currency, and transaction_id. In WooCommerce, hook it into woocommerce_thankyou. In Shopify, drop it in Additional Scripts on the Order Status page (or the Web Pixels API on Plus).

The transaction_id trapGoogle Ads deduplicates conversions by transaction_id — but only within a specific window and only if the same ID reaches every source. Use the raw order ID from your platform. Don't append prefixes, don't hash it, don't strip zeros. The same string must appear in gtag, Enhanced Conversions payload, and any EOC upload. Mismatch → double-counting or Google Ads rejecting the offline record entirely.

This layer catches everyone whose browser loaded gtag and stayed on your domain to the thank-you page. That's the biggest chunk in most accounts — but it's also the layer that suffers most from ad-blockers, third-party payment redirects, and Consent Mode denies.

Layer 2: Enhanced Conversions for Web

Enhanced Conversions for Web adds first-party user data (hashed email, phone, name, address) to the gtag conversion event. Google matches those hashes against logged-in accounts and recovers the click-to-conversion link even when the original cookie was blocked.

The recovery is real but bounded. If the user checked out as a guest and never gave an email that matches a Google account, no recovery happens. If Consent Mode is denied and the entire gtag event never fires, EOC has nothing to enhance — the conversion is lost at layer 1, and enhancement cannot resurrect what never existed.

Turn on the enhancement, not just the setting

Enabling “Enhanced Conversions” in Google Ads UI is not enough. The gtag event on your thank-you page must actually include the user data object. In WooCommerce, read customer_email, billing_first_name, etc., from the order object. In Shopify, pull from Shopify.checkout.customer. Hash client-side with SHA-256 in lowercase, no whitespace.

Verification methodGoogle Ads → Conversions → Enhanced Conversions diagnostics. It shows the percentage of conversions with user-provided data. Under 60% means most events aren't including the hash payload — check that your thank-you gtag actually reads the customer object at fire time.

Layer 3: GA4 Measurement Protocol as Redundancy

Server-to-server events sent via the GA4 Measurement Protocol are the backup line for layer 1. When ad-blockers or Consent Mode kill the client-side gtag, your server (WooCommerce webhook, Shopify order webhook, or your platform's equivalent) can still fire a purchase event with the same transaction_id and value.

Two things matter here. First, this is a GA4 layer, not a Google Ads layer — MP events reach GA4, and Google Ads pulls them via the GA4 link. Second, MP events do not attach session source correctly by default; you'll need the Custom Channel Group workaround covered in the GA4 sessionSource article to keep them out of Direct.

Wire the same client_id

MP events must carry the same client_id that the browser used. Read the _ga cookie server-side (available in every request), extract the client_id, and pass it as client_id in the MP payload. Otherwise GA4 treats the MP event as a new user and de-links attribution entirely.

Layer 4: Enhanced Offline Conversions (The Last-Resort Layer)

Enhanced Offline Conversions upload finalized orders to Google Ads directly, matched against the original gclid stored on the order. This is the layer that recovers whatever the first three missed — guest checkouts, users who never accepted Consent Mode, browsers that blocked everything.

The stack works because EOC has a different failure mode from every other layer. As long as your platform captured gclid at landing and stored it on the order, EOC can upload it days later and Google Ads will still match it. The only requirement is the gclid — no cookie, no gtag fire needed at time of purchase.

01
Capture gclid at landing, not at checkout

On every landing, read ?gclid= and ?_gcl_aw= from the URL. Store in a first-party cookie with 90-day lifetime. On order create, write both to the order's metadata table. Waiting until checkout to capture loses users who browsed for days before buying — the gclid was gone by then.

02
Create a matching offline conversion action

Google Ads → Conversions → New → Import → From clicks. The upload action must exist before you can send data. Note the ID it returns. Wait 4–6 hours after creation before uploading — the action takes time to become writable.

03
Schedule a daily upload with a 24-hour lag

Run the upload job daily against orders from 24–48 hours ago. The lag lets your gtag/EOC layers claim their share first, so EOC only fills the residuals. Uploading in real-time causes over-counting because gtag also fires the same conversion.

04
Use conversion_action + gclid + conversion_date_time

The minimum viable payload. Add first-party user data (hashed email, phone) to make it Enhanced. Set partial_failure=True so one bad row doesn't kill the batch. Log which orders uploaded and which errored, so you can replay.

How to Sequence the Four Layers Without Double-Counting

The single biggest fear teams have with a stack is double-counting. It's avoidable if you configure Google Ads deduplication correctly:

LayerSend transaction_idInclude user data
gtag on thank-youYes — the raw order IDYes — for Enhanced enhancement
MP to GA4Yes — same order IDNo (goes to GA4, not Ads)
EOC uploadYes — same order ID as order_id fieldYes — for Enhanced enhancement

Google Ads deduplicates within an action based on transaction_id. As long as gtag and EOC send the same transaction_id for the same order, only one will count. GA4 is a separate reporting layer entirely — the MP event lives there, doesn't compete with the Ads-side data.

The one setting that breaks everythingBoth your gtag conversion action and your EOC upload action must be the same action. Not two separate actions with the same name — the same conversion action ID. If they're separate actions, dedup can't work, and you'll see roughly 2× conversion volume for a week before you realize what happened.

Expected Recovery from Each Layer, Real Numbers

From an EU WooCommerce client where we deployed the full stack over five weeks:

Baselinegtag only+ EOC Web+ MP+ EOC upload
Conversions attributed vs WooCommerce truth62%74%81%93%

62% to 93% over five weeks, at the cost of one snippet, one CMP toggle, one MP endpoint, and one daily cron. No single layer alone got past 81%. The full stack made the difference.

When to Build the Stack vs. When to Stop at Layer 2

If your monthly ad spend is under $2K and your order volume is under 50/mo, layers 1 and 2 are usually enough — the incremental data from layers 3 and 4 is real but small, and the engineering cost isn't justified. Above $5K/mo or in EU markets with heavy Consent Mode denies, the full stack pays for itself within a month through better Smart Bidding signals and modeled conversion recovery.

We build this stack for every mid-to-large e-commerce account in our Enhanced Conversions service. The full technical playbook is more than fits in a blog post — but the four layers, in this order, are what makes the difference between an attribution rebuild that works and one that plateaus at 75%.

Alex Sterling

Alex Sterling

Founder at Sterling Lab · Google Ads strategist · 42 client accounts across 10 countries