Fix Common Analytics Tracking Issues Fast | OpsBlu Docs

Fix Common Analytics Tracking Issues Fast

Diagnose and resolve the most frequent analytics problems including tags not firing, duplicate events, data discrepancies, and PII leakage risks.

When analytics tracking breaks, the symptoms are often misleading. A missing conversion might be a consent issue, not a broken tag. Inflated pageviews might be a SPA misconfiguration, not a bot problem. This guide covers the most common issues in order of frequency, with a diagnostic approach for each.

Diagnostic Flowchart

Before diving into specific issues, follow this triage sequence:

  1. Open browser DevTools Network tab and filter for your analytics vendor (e.g., collect, bat.bing, tr/ for Meta).
  2. Check if requests are leaving the browser at all. If not, the problem is client-side (consent, CSP, ad blocker, script error).
  3. If requests fire but data is missing in the platform, the problem is server-side (filters, processing rules, property mismatch).
  4. If data exists but looks wrong, the problem is configuration (duplicate tags, wrong parameters, mismatched attribution windows).

Tags Not Firing

This is the single most common issue. The tag exists in your TMS but never executes.

Most consent management platforms (CMPs) default to denying all storage until the user opts in. If your CMP loads before your tag manager, tags that require analytics_storage or ad_storage will silently not fire for users who have not consented.

// Check current consent state in the browser console
// If analytics_storage is "denied", GA4 tags will not set cookies
gtag('consent', 'default', {
  'analytics_storage': 'denied',
  'ad_storage': 'denied'
});

// After user accepts, this must be called:
gtag('consent', 'update', {
  'analytics_storage': 'granted',
  'ad_storage': 'granted'
});

Verify your CMP actually calls the consent update command. Many implementations forget this step and consent remains permanently denied.

Content Security Policy Headers Blocking Scripts

If your server sends a strict Content-Security-Policy header, external tag manager or analytics scripts may be blocked entirely. Check the browser console for errors like Refused to load the script.

# Example CSP header that blocks GTM
Content-Security-Policy: script-src 'self'

# Fix: Add the required domains
Content-Security-Policy: script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com https://connect.facebook.net

Ad Blockers and Privacy Extensions

uBlock Origin, Brave browser, and Firefox Enhanced Tracking Protection all block common analytics endpoints. You cannot "fix" this for those users. Instead, understand your data gap:

  • Expect 15-30% of technical audiences to block analytics
  • Server-side tracking via GTM Server-Side or a measurement protocol endpoint bypasses most client-side blockers
  • Never rely on client-side analytics alone for billing or contractual metrics

Duplicate Events

Double Pageview Counting

The most common cause is loading both a standalone gtag.js snippet and GA4 through GTM. Both fire page_view events independently.

// Check for dual installation in browser console
// If both return true, you have duplicate tracking
console.log('Standalone gtag:', document.querySelector('script[src*="gtag/js"]') !== null);
console.log('GTM container:', document.querySelector('script[src*="googletagmanager.com/gtm"]') !== null);

Fix: Remove the standalone gtag.js snippet and manage GA4 entirely through GTM, or vice versa. Never run both.

Tag Firing on Every SPA Route Change

Single-page applications push history.pushState events that can trigger tags configured for "All Pages." If your GA4 tag fires on both Page View and History Change triggers, every navigation fires the tag twice.

Fix: Use only the History Change trigger for SPA route transitions, and fire the Page View trigger only on the initial load. Alternatively, use GA4's built-in page_view enhanced measurement, which handles this automatically.

Data Discrepancies Between Platforms

GA4 vs Ad Platform Conversion Numbers

It is normal for GA4 and ad platforms to report different conversion counts. This is not a bug. The discrepancy comes from fundamentally different measurement approaches:

  • GA4 uses session-based, last-click attribution by default
  • Google Ads uses click-based attribution with a configurable lookback window (default 30 days)
  • Meta Ads counts view-through conversions (user saw an ad but did not click) and uses a 7-day click / 1-day view window

A 10-30% discrepancy between GA4 and any ad platform is typical. Investigate only if the gap exceeds 40% or changes suddenly.

Session Counting vs Click Counting

GA4 counts sessions. Ad platforms count clicks. One user clicking your ad three times generates three clicks but potentially only one session (if within 30 minutes). Conversely, a single click can generate multiple sessions if the user returns later via a bookmark.

Cross-Domain Tracking Failures

Linker Parameter Not Passing

GA4 cross-domain tracking relies on the _gl query parameter appended to links between domains. If this parameter is missing on cross-domain navigations:

// Verify linker configuration
gtag('config', 'G-XXXXXXXXXX', {
  'linker': {
    'domains': ['store.example.com', 'www.example.com'],
    'accept_incoming': true,
    'decorate_forms': true  // Required for form-based navigations
  }
});

Common causes of failure: JavaScript redirects that strip query parameters, server-side redirects that drop the _gl parameter, or link click handlers that reconstruct the URL without the linker.

Missing Referral Exclusions

If your checkout lives on a subdomain or third-party domain (e.g., Shopify checkout), add it to the referral exclusion list in GA4. Without this, returning from checkout starts a new session attributed to the checkout domain as a referral, breaking your attribution.

GA4 path: Admin > Data Streams > Configure tag settings > List unwanted referrals.

Event Parameter Issues

Wrong Data Types

GA4 silently drops parameters sent with the wrong data type. The value parameter must be a number, not a string. The items parameter must be an array, not an object.

// WRONG: value as string
gtag('event', 'purchase', { value: '49.99', currency: 'USD' });

// CORRECT: value as number
gtag('event', 'purchase', { value: 49.99, currency: 'USD' });

// WRONG: single item not in array
gtag('event', 'purchase', { items: { item_id: 'SKU1' } });

// CORRECT: items as array
gtag('event', 'purchase', { items: [{ item_id: 'SKU1' }] });

Missing Required Parameters

Certain GA4 events require specific parameters to populate reports. For example, purchase events without transaction_id will not appear in the Monetization reports. The currency parameter is required alongside value for any revenue tracking.

PII Leakage Risks

Emails in URLs

If your site passes email addresses in URLs (e.g., /confirm?email=user@example.com), GA4 collects the full page URL by default, sending PII to Google. This violates Google's Terms of Service and most privacy regulations.

// Scrub PII from URLs before they reach analytics
// In GTM, use a Custom JavaScript variable for Page URL:
function() {
  var url = window.location.href;
  // Remove email parameters
  url = url.replace(/([?&])(email|user_email|mail)=[^&]*/gi, '$1$2=REDACTED');
  return url;
}

Form Data in Events

Never pass form field values directly into analytics events without sanitizing them. Names, phone numbers, and addresses can all end up in your GA4 property if your data layer blindly forwards form inputs.

Tags that fire on the Initialization - All Pages trigger in GTM execute before consent banners render. If your analytics tags use this trigger, they fire before the user has a chance to consent or deny.

Fix: Move analytics tags to fire on All Pages (which respects consent mode) or use GTM's built-in consent checks. Only essential consent-initialization scripts should use the Initialization trigger.

Not Respecting Opt-Out

Some implementations update the consent state correctly on the initial page but fail to persist the denial across navigations. Test your opt-out flow end-to-end: deny consent, navigate to three pages, close the browser, return, and verify that consent remains denied and no analytics requests fire.