Files
gh-henkisdabro-wookstar-cla…/skills/ga4-privacy-compliance/references/consent-mode-implementation-examples.md
2025-11-29 18:32:40 +08:00

14 KiB

Consent Mode v2 Implementation Examples

Basic gtag.js Implementation

Complete Setup (EU GDPR Compliant)

<!DOCTYPE html>
<html>
<head>
  <!-- Step 1: Set default consent BEFORE any tags load -->
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}

    // Default consent for EU users (denied)
    gtag('consent', 'default', {
      'ad_storage': 'denied',
      'ad_user_data': 'denied',
      'ad_personalization': 'denied',
      'analytics_storage': 'denied',
      'wait_for_update': 500  // Wait 500ms for consent update
    });
  </script>

  <!-- Step 2: Load gtag.js -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>

  <!-- Step 3: Configure GA4 -->
  <script>
    gtag('config', 'G-XXXXXXXXXX');
  </script>

  <!-- Your consent banner library here -->
</head>
<body>
  <!-- Consent banner UI -->
  <div id="consent-banner" style="display:none;">
    <p>We use cookies to improve your experience.</p>
    <button onclick="acceptAllConsent()">Accept All</button>
    <button onclick="acceptAnalyticsOnly()">Analytics Only</button>
    <button onclick="rejectAllConsent()">Reject All</button>
  </div>

  <script>
    // Show banner if no consent stored
    if (!localStorage.getItem('consentStatus')) {
      document.getElementById('consent-banner').style.display = 'block';
    } else {
      // Apply stored consent
      applyStoredConsent();
    }

    function acceptAllConsent() {
      gtag('consent', 'update', {
        'ad_storage': 'granted',
        'ad_user_data': 'granted',
        'ad_personalization': 'granted',
        'analytics_storage': 'granted'
      });
      localStorage.setItem('consentStatus', 'all');
      document.getElementById('consent-banner').style.display = 'none';
    }

    function acceptAnalyticsOnly() {
      gtag('consent', 'update', {
        'ad_storage': 'denied',
        'ad_user_data': 'denied',
        'ad_personalization': 'denied',
        'analytics_storage': 'granted'
      });
      localStorage.setItem('consentStatus', 'analytics');
      document.getElementById('consent-banner').style.display = 'none';
    }

    function rejectAllConsent() {
      gtag('consent', 'update', {
        'ad_storage': 'denied',
        'ad_user_data': 'denied',
        'ad_personalization': 'denied',
        'analytics_storage': 'denied'
      });
      localStorage.setItem('consentStatus', 'none');
      document.getElementById('consent-banner').style.display = 'none';
    }

    function applyStoredConsent() {
      const status = localStorage.getItem('consentStatus');
      if (status === 'all') {
        acceptAllConsent();
      } else if (status === 'analytics') {
        acceptAnalyticsOnly();
      } else {
        rejectAllConsent();
      }
    }
  </script>
</body>
</html>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// EU/EEA: Default denied
gtag('consent', 'default', {
  'ad_storage': 'denied',
  'ad_user_data': 'denied',
  'ad_personalization': 'denied',
  'analytics_storage': 'denied'
}, {
  'region': ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB', 'IS', 'LI', 'NO']
});

// US (except California): Default granted
gtag('consent', 'default', {
  'ad_storage': 'granted',
  'ad_user_data': 'granted',
  'ad_personalization': 'granted',
  'analytics_storage': 'granted'
});

// California (CCPA): Analytics granted, ads denied
gtag('consent', 'default', {
  'ad_storage': 'denied',
  'ad_user_data': 'denied',
  'ad_personalization': 'denied',
  'analytics_storage': 'granted'
}, {
  'region': ['US-CA']
});

Google Tag Manager Implementation

Method 1: Custom HTML Tags

Tag 1: Consent Initialization (Fires First)

<!-- Tag Name: Consent Mode - Initialization -->
<!-- Tag Type: Custom HTML -->
<!-- Firing Trigger: Consent Initialization - All Pages -->
<!-- Advanced Settings → Tag firing priority: 999 -->

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}

  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'denied',
    'wait_for_update': 500
  });
</script>

Tag 2: Consent Update - Accept All

<!-- Tag Name: Consent Mode - Accept All -->
<!-- Tag Type: Custom HTML -->
<!-- Firing Trigger: Custom Event - consent_granted -->

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}

  gtag('consent', 'update', {
    'ad_storage': 'granted',
    'ad_user_data': 'granted',
    'ad_personalization': 'granted',
    'analytics_storage': 'granted'
  });
</script>

Tag 3: Consent Update - Analytics Only

<!-- Tag Name: Consent Mode - Analytics Only -->
<!-- Tag Type: Custom HTML -->
<!-- Firing Trigger: Custom Event - consent_analytics -->

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}

  gtag('consent', 'update', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'granted'
  });
</script>

Method 2: Using CMP Template (Cookiebot Example)

  1. Install Cookiebot Template:

    • GTM Templates → Template Gallery
    • Search "Cookiebot"
    • Add to workspace
  2. Configure Cookiebot Tag:

    • Tag Type: Cookiebot CMP (Community Template)
    • Cookiebot ID: Your Cookiebot ID
    • Default consent: All denied
    • Trigger: Consent Initialization - All Pages
    • Priority: 999
  3. Cookiebot auto-handles:

    • Default consent setting
    • Consent updates on user choice
    • Consent persistence
    • Regional settings

JavaScript Event Listeners

// Monitor consent state changes
window.addEventListener('consent-update', function(event) {
  console.log('Consent updated:', event.detail);

  // Send custom event to analytics
  gtag('event', 'consent_update', {
    'consent_status': event.detail.analytics_storage,
    'timestamp': new Date().toISOString()
  });
});

// Trigger on user action
function updateConsent(status) {
  gtag('consent', 'update', status);

  // Dispatch event for other scripts
  window.dispatchEvent(new CustomEvent('consent-update', {
    detail: status
  }));
}

CCPA / Global Privacy Control (GPC)

Detecting and Honoring GPC

window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// Check for Global Privacy Control signal
if (navigator.globalPrivacyControl === true) {
  // User has GPC enabled - deny advertising
  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'granted'  // Analytics OK
  });
} else {
  // No GPC signal - use standard defaults
  gtag('consent', 'default', {
    'ad_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied',
    'analytics_storage': 'denied'
  });
}

GTM Variable for GPC

Variable Configuration:

  • Variable Type: Custom JavaScript
  • Name: "GPC - Global Privacy Control"
function() {
  return navigator.globalPrivacyControl === true ? 'enabled' : 'disabled';
}

Use in consent logic to conditionally set consent.

import requests
import json

MEASUREMENT_ID = "G-XXXXXXXXXX"
API_SECRET = "your_api_secret"
ENDPOINT = f"https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"

def send_with_consent(client_id, event_name, consent_status):
    payload = {
        "client_id": client_id,
        "consent": {
            "ad_storage": consent_status.get('ad_storage', 'denied'),
            "analytics_storage": consent_status.get('analytics_storage', 'denied'),
            "ad_user_data": consent_status.get('ad_user_data', 'denied'),
            "ad_personalization": consent_status.get('ad_personalization', 'denied')
        },
        "events": [{
            "name": event_name,
            "params": {}
        }]
    }

    response = requests.post(ENDPOINT, json=payload)
    return response.status_code == 204

# Example: User has granted analytics only
send_with_consent(
    client_id="client_123",
    event_name="purchase",
    consent_status={
        "ad_storage": "denied",
        "analytics_storage": "granted",
        "ad_user_data": "denied",
        "ad_personalization": "denied"
    }
)

React Example

import { useEffect, useState } from 'react';

function ConsentManager() {
  const [showBanner, setShowBanner] = useState(false);

  useEffect(() => {
    // Initialize consent on app load
    window.dataLayer = window.dataLayer || [];
    function gtag(){window.dataLayer.push(arguments);}

    // Set default
    gtag('consent', 'default', {
      'ad_storage': 'denied',
      'analytics_storage': 'denied',
      'ad_user_data': 'denied',
      'ad_personalization': 'denied'
    });

    // Check for stored consent
    const storedConsent = localStorage.getItem('userConsent');
    if (storedConsent) {
      const consent = JSON.parse(storedConsent);
      gtag('consent', 'update', consent);
    } else {
      setShowBanner(true);
    }
  }, []);

  const acceptAll = () => {
    const consent = {
      'ad_storage': 'granted',
      'analytics_storage': 'granted',
      'ad_user_data': 'granted',
      'ad_personalization': 'granted'
    };

    window.gtag('consent', 'update', consent);
    localStorage.setItem('userConsent', JSON.stringify(consent));
    setShowBanner(false);
  };

  const rejectAll = () => {
    const consent = {
      'ad_storage': 'denied',
      'analytics_storage': 'denied',
      'ad_user_data': 'denied',
      'ad_personalization': 'denied'
    };

    window.gtag('consent', 'update', consent);
    localStorage.setItem('userConsent', JSON.stringify(consent));
    setShowBanner(false);
  };

  if (!showBanner) return null;

  return (
    <div className="consent-banner">
      <p>We use cookies to improve your experience.</p>
      <button onClick={acceptAll}>Accept</button>
      <button onClick={rejectAll}>Reject</button>
    </div>
  );
}

export default ConsentManager;

DebugView Verification

// Enable debug mode for testing
gtag('config', 'G-XXXXXXXXXX', {
  'debug_mode': true
});

// Set consent
gtag('consent', 'update', {
  'analytics_storage': 'granted'
});

// Send test event
gtag('event', 'test_consent', {
  'test_parameter': 'test_value'
});

// Check in DebugView:
// 1. Event appears
// 2. Event details show consent status
// 3. Look for "gcs" parameter (Google Consent State)

Console Verification

// Check consent state in console
dataLayer.filter(item => item[0] === 'consent')

// Expected output:
// [
//   ['consent', 'default', {...}],
//   ['consent', 'update', {...}]
// ]

Before Consent (analytics_storage = denied):

  • Open DevTools → Application → Cookies
  • Should NOT see _ga, _ga_* cookies

After Consent (analytics_storage = granted):

  • _ga cookie present
  • _ga_<container-id> cookie present
// Your custom consent preferences
const userPreferences = {
  necessary: true,        // Always on
  analytics: true,        // User accepted
  marketing: false,       // User denied
  personalization: true   // User accepted
};

// Map to Google consent parameters
const consentMapping = {
  ad_storage: userPreferences.marketing ? 'granted' : 'denied',
  ad_user_data: userPreferences.marketing ? 'granted' : 'denied',
  ad_personalization: userPreferences.personalization ? 'granted' : 'denied',
  analytics_storage: userPreferences.analytics ? 'granted' : 'denied'
};

// Update consent
gtag('consent', 'update', consentMapping);
function showConsentSettings() {
  // Show modal/settings page
  document.getElementById('consent-settings-modal').style.display = 'block';
}

function updateConsentPreferences(newPreferences) {
  gtag('consent', 'update', newPreferences);
  localStorage.setItem('userConsent', JSON.stringify(newPreferences));

  // Optional: Reload page to apply changes
  // window.location.reload();
}

// Example: User clicks "Withdraw Consent"
function withdrawConsent() {
  const deniedConsent = {
    'ad_storage': 'denied',
    'analytics_storage': 'denied',
    'ad_user_data': 'denied',
    'ad_personalization': 'denied'
  };

  updateConsentPreferences(deniedConsent);

  // Delete existing GA cookies
  document.cookie.split(";").forEach(function(c) {
    if (c.trim().startsWith('_ga')) {
      document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
    }
  });
}

Compliance Checklist

  • Default consent set to "denied" before tags load
  • Consent banner shown before tracking
  • User can accept/reject consent
  • Consent choices persist across sessions
  • Consent can be withdrawn/changed later
  • All 4 v2 parameters included (ad_storage, analytics_storage, ad_user_data, ad_personalization)
  • Regional settings configured if needed
  • GPC honored for CCPA compliance
  • Consent status tested in DebugView
  • Cookie behavior verified (no cookies before consent)
  • Privacy policy updated with consent information