Skip to content
Lingows
Faceted iceberg over a wireframe foundation, for conversion tracking that survives a redesign.

Tutorial

Set up conversion tracking that survives

Conversion tracking breaks quietly: a redesign changes a button's DOM structure, consent mode gets misconfigured, or two tags fire the same event twice. This walks through building tracking that keeps working after the next site change, and setting realistic expectations for cross-platform numbers.

  • Intermediate
  • 14 minute read
  • Last reviewed 2026-08-10

Most conversion tracking is set up once, during launch, and never documented. Six months later nobody knows which events are real conversions, which are duplicates, and why the ad platform reports twice as many leads as GA4. That gap is not usually a bug. It is measurement methodology.

The fix is a written event taxonomy, a small number of true key events, explicit deduplication logic when both client and server side tracking exist, and consent handling that does not silently drop data for a large share of visitors.

Before you start

  • A GA4 property already connected to the site
  • Access to Google Tag Manager or direct template edits
  • Basic familiarity with dataLayer pushes and gtag syntax

Steps

Work through it in order

  1. Step 1

    Write the event taxonomy before touching any tags

    List every meaningful user action, its trigger condition, and its parameters in a shared document before implementing anything. Without this, different team members name the same action differently across GA4, the ad platform, and any CRM integration, and reconciliation becomes guesswork.

    Event taxonomy (excerpt)
    event_name        trigger                          key_params
    form_submit_lead  contact form success response   form_id, page_path
    call_click        tel: link click                 page_path
    quote_request     multi-step form final submit     service_type, lead_value
    scroll_75         75% scroll depth on any page     page_path
  2. Step 2

    Fire events through dataLayer, not inline onclick handlers

    Pushing to dataLayer keeps tracking logic out of your application code and lets Tag Manager own trigger conditions. This also means a marketing team member can adjust triggers without a code deploy, which matters once the taxonomy grows past a handful of events.

    dataLayer push on form success
    document.querySelector("#contact-form").addEventListener("submit", () => {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: "form_submit_lead",
        form_id: "contact-form",
        page_path: window.location.pathname
      });
    });
  3. Step 3

    Mark true key events in GA4, sparingly

    GA4 lets you flag any event as a key event (formerly 'conversion'). Flag only actions that represent real business value: a submitted lead form, a booked call, a completed purchase. Marking scroll depth or outbound clicks as key events dilutes reporting and confuses anyone reading it later.

    Send the event to GA4 via gtag
    gtag("event", "form_submit_lead", {
      form_id: "contact-form",
      page_path: window.location.pathname,
      value: 1,
      currency: "USD"
    });
  4. Step 4

    Deduplicate when server side events exist alongside client side

    If you send the same conversion from both the browser and a server via the Measurement Protocol, GA4 needs a shared identifier to avoid double counting. Generate one transaction or event ID at the point of the action and pass it through both paths.

    Shared event ID for deduplication
    // client side
    const eventId = crypto.randomUUID();
    gtag("event", "form_submit_lead", { form_id: "contact-form" });
    fetch("/api/track-server-side", {
      method: "POST",
      body: JSON.stringify({ eventId, event: "form_submit_lead" })
    });
    
    // server side, sent via Measurement Protocol, reusing the same eventId
    // so GA4's deduplication window can match and discard the duplicate
  5. Step 5

    Send server side events for actions the browser cannot see reliably

    Ad blockers, tracking prevention, and third party cookie restrictions cause client side tags to under-report. For high value actions like a closed sale confirmed later in a CRM, send the event server side directly to GA4's Measurement Protocol, keyed to the client_id captured at the original visit.

    Server side Measurement Protocol call
    POST https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXX&api_secret=SECRET
    Content-Type: application/json
    
    {
      "client_id": "1234567890.0987654321",
      "events": [
        {
          "name": "purchase_confirmed",
          "params": { "value": 850, "currency": "USD", "transaction_id": "ORD-4471" }
        }
      ]
    }
  6. Step 6

    Handle consent state before any tag fires

    Google's consent mode requires setting default consent state before the GA4 tag loads, then updating it once a user responds to your consent banner. Getting the order wrong either fires tags before consent is known or blocks all measurement including consent-safe modeled conversions.

    Consent mode default and update
    gtag("consent", "default", {
      ad_storage: "denied",
      analytics_storage: "denied",
      wait_for_update: 500
    });
    
    // after the user accepts in your consent banner
    gtag("consent", "update", {
      ad_storage: "granted",
      analytics_storage: "granted"
    });
  7. Step 7

    Reconcile ad platform numbers against GA4 without expecting a match

    Google Ads, Meta, and GA4 each use different attribution windows, different identity resolution, and different definitions of a conversion. A campaign showing 40 conversions in Google Ads and 30 in GA4 is not automatically a tracking bug. Check attribution window length and click-through versus view-through settings before assuming something is broken.

    Document the expected variance range for each platform pairing once, based on their attribution settings, so the next person does not re-investigate a known and acceptable gap every month.

  8. Step 8

    Verify with a real test conversion, not the debug view alone

    GA4's DebugView confirms events fire and parameters are correct, but it does not confirm the event graduated to a key event or that server side deduplication actually worked. Submit one real test conversion through the full funnel and confirm it appears once, correctly attributed, within 24 to 48 hours.

Pitfalls

What goes wrong in practice

  • Marking every dataLayer event as a GA4 key event, which makes the conversions report meaningless within a few weeks.
  • Firing the GA4 tag before consent mode's default state is set, which can cause tags to run unconditionally in some Tag Manager configurations.
  • Sending server side and client side events for the same action with no shared ID, producing a silent doubling of reported conversions.
  • Expecting Google Ads, Meta, and GA4 to report identical numbers. They measure different things by design and will not converge.
  • Never revisiting the event taxonomy after a redesign changes form structures or button markup, leaving triggers silently broken.

Why do my Google Ads conversions never match GA4?

They use different attribution models. Google Ads often counts view-through conversions and uses its own click attribution window, while GA4 uses its own model based on the attribution settings in the property. A gap is expected. The goal is a documented, stable gap, not an identical number across platforms.

  • Write the event taxonomy before implementing any tags
  • Deduplicate client and server events with a shared event ID
  • Set consent defaults before any tag fires, then update on user response

Rather have this done for you

We run these steps as part of a program, with the checks automated where they can be.