Skip to content
ceaksan

Google Consent Mode v2 Implementation: gtag, GTM, Meta Pixel and Platform Integrations

Consent Mode v2 now uses a four-signal model (ad_storage, analytics_storage, ad_user_data, ad_personalization). Gtag code examples, GTM template, Meta Pixel, HubSpot and Hotjar consent implementations.

Sep 15, 2025 7 min read Updated: Apr 22, 2026
TL;DR

Google Consent Mode v2 uses a four-signal model: ad_storage, analytics_storage, ad_user_data, ad_personalization. Mandatory since March 2024 for ads personalization on EEA traffic. This guide covers gtag code, GTM templates, and Meta Pixel, HubSpot, Hotjar implementations.

Google Consent Mode v2 (mandatory for EEA and UK traffic since March 2024) operates on a four-signal model: ad_storage, analytics_storage, ad_user_data, ad_personalization. The installation pattern is the same: default state denied fires when the page loads, and the consent tool updates signals via update after the user makes a choice. This guide covers gtag, GTM, Meta Pixel, HubSpot and Hotjar implementations with current code.

tip

This is a technical implementation guide for Consent Mode v2, not legal advice. KVKK (TR) and GDPR (EEA) obligations must be evaluated against your own jurisdiction.

Signalv1v2Controls
ad_storageRead/write consent for advertising cookies
analytics_storageConsent for analytics cookies
ad_user_dataWhether user data (hashed email, ads click IDs) can be sent to Google
ad_personalizationWhether data can be used for remarketing and personalized ads
functionality_storageCookies for site functionality
personalization_storageOn-site personalization cookies
security_storageSecurity/fraud detection cookies

From March 2024 Google enforced ad_user_data and ad_personalization on EEA and UK traffic for ads personalization, remarketing audiences and modelled conversions. Accounts still on v1 lose these features.

Related up-to-date coverage:

Every gtag consent installation has two phases: the default command defines the initial state on page load, then the update command applies the user’s choice after the consent tool captures it. The default call must run before the Google Tag (gtag.js) loads; otherwise the first network requests fire without a consent state.

Cookie Consent Banner Example
Cookie Consent Banner Example

Default State: All Signals Denied

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

gtag("consent", "default", {
  ad_storage: "denied",
  analytics_storage: "denied",
  ad_user_data: "denied",
  ad_personalization: "denied",
  wait_for_update: 500,
});

wait_for_update: 500 tells gtag to wait 500ms for a consent update. If the consent tool resolves within that window, the first hit fires consent-aware. Critical for asynchronous consent tools.

Region-Based Default

KVKK explicit-consent requirements apply to TR; GDPR covers EEA and UK. Other regions may use different defaults. region accepts ISO 3166-2 codes (country: TR, sub-region: US-CA).

gtag("consent", "default", {
  ad_storage: "denied",
  analytics_storage: "denied",
  ad_user_data: "denied",
  ad_personalization: "denied",
  region: [
    "TR",
    "ES",
    "GB",
    "DE",
    "FR",
    "IT",
    "NL",
    "BE",
    "SE",
    "DK",
    "FI",
    "NO",
    "PL",
    "AT",
    "CH",
    "IE",
  ],
});

gtag("consent", "default", {
  ad_storage: "granted",
  analytics_storage: "granted",
  ad_user_data: "granted",
  ad_personalization: "granted",
});

The first call applies the denied default to the listed regions; the second call sets a catch-all granted default for everywhere else. Ordering matters; placing the general default last is the common pattern.

note

region is only valid on the default command; it cannot be used on update.

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

  // Default state
  gtag("consent", "default", {
    ad_storage: "denied",
    analytics_storage: "denied",
    ad_user_data: "denied",
    ad_personalization: "denied",
    wait_for_update: 500,
  });

  // URL passthrough: carries gclid/dclid/_gl across pages even without consent
  gtag("set", "url_passthrough", true);

  // Ads data redaction: strips ads click IDs from network requests while ad_storage is denied
  gtag("set", "ads_data_redaction", true);

  // Consent tool integration
  ConsentTool.on("consent-tool-initialized", function (consentObject) {
    gtag("consent", "update", {
      ad_storage: consentObject.ADVERTISING === "ACCEPT" ? "granted" : "denied",
      analytics_storage:
        consentObject.ANALYTICS === "ACCEPT" ? "granted" : "denied",
      ad_user_data:
        consentObject.ADVERTISING === "ACCEPT" ? "granted" : "denied",
      ad_personalization:
        consentObject.ADVERTISING === "ACCEPT" ? "granted" : "denied",
    });
  });

  ConsentTool.on("consent-saved", function (consentObject) {
    gtag("consent", "update", {
      ad_storage: consentObject.ADVERTISING === "ACCEPT" ? "granted" : "denied",
      analytics_storage:
        consentObject.ANALYTICS === "ACCEPT" ? "granted" : "denied",
      ad_user_data:
        consentObject.ADVERTISING === "ACCEPT" ? "granted" : "denied",
      ad_personalization:
        consentObject.ADVERTISING === "ACCEPT" ? "granted" : "denied",
    });
  });
</script>

<script
  async
  src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    dataLayer.push(arguments);
  }
  gtag("js", new Date());
  gtag("config", "GA_MEASUREMENT_ID");
  gtag("config", "AW-CONVERSION_ID");
</script>

ad_storage Denied Behaviour

  • No new advertising cookies are created, and existing advertising cookies are not read.
  • Third-party cookies are used only for spam and fraud detection.
  • GA4 does not read advertising cookies; Google Signals does not fire.
  • IP addresses are used only for geolocation.

analytics_storage Denied Behaviour

  • GA4 cannot read or write its first-party cookies (_ga, _ga_<property>).
  • Hits still fire; every pageview sends with a fresh anonymous identifier.
  • The gcs parameter carries the consent state.

ad_user_data Denied Behaviour

  • Hashed user data (email, phone) is not sent to Google Ads or Meta CAPI.
  • Enhanced Conversions stop working.
  • Ads click IDs (gclid, gbraid, wbraid) are not forwarded.

ad_personalization Denied Behaviour

  • The user is not added to remarketing audiences.
  • Personalized ads are not served.
  • The user is not used as a seed for lookalike audiences.

Consent Mode dictates how Google tags behave but does not manage consent itself; a consent tool is required to capture the user’s choice. See the tool selection section below.

Play

Google Tag Manager (GTM)

GTM offers two paths for Consent Mode v2: the pre-built Template Gallery or Custom HTML.

Google Tag Manager and Cookie Tools
Google Tag Manager and Cookie Tools

Simo Ahava’s Consent Mode (Google tags) template supports v2 signals1. Add it via Google Tag Manager > Templates > Tag Templates > Search Gallery, then create a tag via Tags > New > Tag Configuration > Consent Mode (Google tags).

The template lets you configure default state and region-specific overrides in the UI. The CMP side is read through a Variable > Data Layer Variable or a CMP-specific template. In practice: bind this tag to the Consent Initialization - All Pages trigger so it runs before every other tag.

Custom HTML Approach

If you prefer not to use the template, bind a Custom HTML tag to the Consent Initialization - All Pages trigger and inject the gtag code above directly. This approach lets you write CMP-specific event handlers (for example, Efilli, Mobildev, Cookiebot) without intermediaries.

For platform-specific examples where a store’s native Cookie Law module must be bridged with Consent Mode v2: T-Soft Cookie Law and Google Consent Mode v2 Incompatibility.

Each tag in GTM has Consent Settings > No additional consent required or Require additional consent for tag to fire. Recommended settings for v2:

  • Google Ads Conversion Tracking: ad_storage, ad_user_data required
  • Google Ads Remarketing: ad_storage, ad_user_data, ad_personalization required
  • GA4 Configuration: analytics_storage required (built-in consent check)
  • Meta Pixel: ad_storage required

Meta Pixel (Replacing Facebook Analytics)

Facebook Analytics was shut down on June 30, 2021. On the Meta side consent is now handled through Meta Pixel and Meta Conversions API (CAPI). The Pixel exposes a native consent parameter:

// Before the Pixel loads
fbq("consent", "revoke");

// Pixel load
fbq("init", "<PIXEL_ID>");
fbq("track", "PageView");

// When consent is granted
fbq("consent", "grant");

On the Meta CAPI side the consent state travels in the event payload as opt_out: true. Dual-send (Pixel + CAPI) and event_id-based deduplication detail: Google Ads Enhanced Conversions Guide.

HubSpot

The HubSpot tracking code ships with its own opt-out mechanism. Banner integration runs through _hsp.push(['revokeCookieConsent']) or _hsp.push(['addPrivacyConsentListener', callback]).

Hubspot cookie management
Hubspot cookie management

Main cookies set by HubSpot:

CookieDurationPurpose
__hs_opt_out13 monthsRemembers the user’s opt-out choice
__hs_do_not_track13 monthsRuns the tracking code in anonymous mode
__hs_initial_opt_in7 daysPrevents the banner from reappearing
__hs_cookie_cat_pref13 monthsStores which cookie categories were accepted
__hstc, hubspotutk, __hssc, __hssrcVariesAnalytics cookies

HubSpot consent is configured at Settings > Privacy & Consent > Cookies; the _hsp API also allows integration with an external CMP. A revoke button example:

<button
  type="button"
  onClick="(function(){
    var _hsp = window._hsp = window._hsp || [];
    _hsp.push(['revokeCookieConsent']);
  })()"
>
  Remove cookies
</button>

Hotjar

Hotjar’s default behaviour is to load its tracking code and set cookies immediately. To prevent pre-consent loading the snippet must be conditionally injected, or _hjSettings must be set with an opt-out parameter.

To prevent form inputs from being captured in recordings, use the data-hj-allow or data-hj-suppress attributes.

Microsoft Clarity

By default Clarity uses cookies and localStorage for heatmaps and session recording. It has no native Consent Mode v2 integration; Clarity is controlled through its own clarity('consent') API. Before consent is captured, Clarity should not be injected; or if injected, no data should be collected until the consent signal is passed.

// On page load, consent state is unknown
// Do not inject the Clarity script before clarity() is invoked

// When consent is granted, load Clarity and signal consent
clarity("consent");

// When consent is revoked later
clarity("consent", false);

Clarity’s GTM template supports CMP integration; with Custom HTML, the Clarity init snippet must be added to the DOM only after consent state is granted. Clarity also supports masking form inputs with the data-clarity-mask attribute (similar to Hotjar’s data-hj-suppress).

Shopify Customer Privacy API

On Shopify stores, consent management runs through window.Shopify.customerPrivacy. The API must be loaded via loadFeatures before use; reading or writing Shopify cookies directly (such as _tracking_consent) is unsupported and will break on version bumps.

window.Shopify.loadFeatures(
  [{ name: "consent-tracking-api", version: "0.1" }],
  (error) => {
    if (error) {
      // handle error
    }
  },
);

Four Allowed methods check processing permission; they combine merchant settings, visitor location, and user choice:

window.Shopify.customerPrivacy.preferencesProcessingAllowed();
window.Shopify.customerPrivacy.analyticsProcessingAllowed();
window.Shopify.customerPrivacy.marketingAllowed();
window.Shopify.customerPrivacy.saleOfDataAllowed();

When consent changes (via setTrackingConsent), the visitorConsentCollected event fires. Since the listener only receives updates on change, current state must be fetched via the methods above on page load.

document.addEventListener("visitorConsentCollected", (event) => {
  // event.detail: { marketingAllowed, saleOfDataAllowed, analyticsAllowed, preferencesAllowed }
});

Consent is recorded only on visitor interaction (accept/deny via banner), never written automatically. The sale_of_data parameter is not an opt-in category; it belongs to a separate CCPA/VCDPA-style opt-out flow:

// Standard consent (analytics/marketing/preferences)
window.Shopify.customerPrivacy.setTrackingConsent(
  { analytics: true, marketing: true, preferences: true },
  () => {},
);

// Data sale opt-out (separate flow)
window.Shopify.customerPrivacy.setTrackingConsent(
  { sale_of_data: false },
  () => {},
);

The GPC (Global Privacy Control) signal is honoured automatically in opt-out regions and cannot be overridden via setTrackingConsent.

Useful helper methods:

  • shouldShowBanner(): returns true if consent is not yet captured and the visitor is in a region that requires a banner.
  • currentVisitorConsent(): returns { marketing, analytics, preferences, sale_of_data } with values 'yes', 'no', or '' (not yet decided).
  • getRegion(): ISO 3166-2 code (e.g. USCA, GBENG); empty string if location can’t be determined.
  • saleOfDataRegion(): true if visitor is in an opt-out region.

For custom GTM setups, listen for the visitorConsentCollected event and trigger a Consent Mode v2 update command. Hydrogen and other headless storefronts must pass additional parameters to setTrackingConsent (headlessStorefront: true, checkoutRootDomain, storefrontRootDomain, storefrontAccessToken) along with Storefront API token provisioning and a CSP update. For Hydrogen, the Hydrogen Analytics package is the preferred integration path.

The inventory of cookies Shopify sets on merchant storefronts (strictly necessary, analytics, marketing categories, plus _tracking_consent, _shopify_essential, _shopify_analytics, _shopify_marketing, _shopify_s, _shopify_y) is documented in the official Shopify Cookie Policy. Use this list as the baseline for CMP category mapping.

Client-side Consent Mode v2 signals are forwarded to the sGTM container via event data. Server-side tags read the consent state via consent_state or the consent field on the event and fire conditionally. For server-side tags like Meta CAPI and Google Ads Conversions, when ad_user_data: 'denied', user data (hashed email, phone) must be stripped from the event payload before the tag fires.

// sGTM server-side tag custom template (example)
const events = getAllEventData();
const consentState = events.consent_state || {};

if (consentState.ad_user_data === "denied") {
  delete events.user_data.em;
  delete events.user_data.ph;
}

In practice Meta’s official sGTM Tag Template handles this distinction; when ad_storage is denied, the CAPI call still goes out but with modified action_source and ads click IDs stripped. sGTM’s role in the agency stack and the runtime reality: sGTM Hosting Decision Matrix.

The GA4 BigQuery export schema stores consent state in the privacy_info struct:

  • privacy_info.analytics_storage: Yes / No / Unset
  • privacy_info.ads_storage: Yes / No / Unset
  • privacy_info.uses_transient_token: true / false

Per-event consent state enables consent-segmented analysis on BigQuery. To compare modelled vs. observed conversions while keeping consent-aware users separate in attribution modelling:

SELECT
  event_name,
  privacy_info.analytics_storage,
  COUNT(*) AS event_count
FROM
  `project.analytics_XXXXXX.events_*`
WHERE
  _TABLE_SUFFIX BETWEEN '20260401' AND '20260422'
GROUP BY
  1, 2
ORDER BY
  event_count DESC

On an account running Advanced mode, events arriving with privacy_info.analytics_storage = 'No' are the cookieless pings; Google uses them for modelling, and user_pseudo_id is a transient per-event value. For BigQuery-based multi-channel attribution and post-consent recovery, see the attribution cluster.

Tool Selection: The 4-Category Matrix

The first version of this post reviewed tools (CookieBot, CookieFirst, CookieHub, CookiePro, CookieScript, CookieYes). The tool market has moved on significantly since then; pricing and feature sets are no longer current, and some products are gone. An up-to-date, agency-framed 4-category decision matrix (local TR, global premium, Cloudflare native, self-hosted) lives in the pillar piece.

Short preview of the categories:

  • Local (TR): Efilli, Mobildev, Consentify, Cookieyes TR.
  • Global premium: Cookiebot, OneTrust, Termly, Osano, Iubenda.
  • Cloudflare native: Zaraz.
  • Self-hosted: Klaro, CookieConsent (Orest Bida), Civic, tarteaucitron.

Footnotes

  1. Consent Mode For Google Tags. Simo Ahava’s blog
Key Takeaways
  • 01 Consent Mode v2 carries four signals: ad_storage, analytics_storage, ad_user_data, ad_personalization. The first two came from v1; ad_user_data and ad_personalization were added with v2.
  • 02 Default state must be 'denied'; gtag('consent', 'default', {...}) is called before the Google Tag loads. After the consent tool captures the user's choice, signals are updated via 'update'.
  • 03 On EEA traffic Consent Mode v2 is mandatory for ads personalization (remarketing) and modelled conversions. Basic mode only transmits the consent signal; Advanced mode sends cookieless pings enabling modelling.
  • 04 In GTM it is installed either via the Template Gallery's 'Consent Mode (Google tags)' template or via Custom HTML. Binding the tag to the Consent Initialization trigger guarantees ordering.
  • 05 For Meta Pixel, fbq('consent', 'revoke'|'grant') must be called before the Pixel loads; init happens after consent. HubSpot and Hotjar handle consent via their own opt-out cookies.
Frequently Asked Questions (FAQ)
+ What changed between Consent Mode v1 and v2?

v1 had two signals (ad_storage, analytics_storage). v2 added ad_user_data and ad_personalization. ad_user_data controls whether user data (hashed PII, ads click IDs) can be sent to Google, and ad_personalization controls whether that data can be used for remarketing and personalized ads.

+ When did Consent Mode v2 become mandatory?

From March 2024 Google required v2 signals for ads personalization, remarketing and modelled conversions on EEA and UK traffic. Non-compliant accounts lose audience and conversion modelling, and remarketing lists stop growing.

+ Basic mode vs. Advanced mode?

Basic mode blocks Google tags entirely when consent is denied; only the consent state signal is sent. Advanced mode still sends a cookieless ping when denied, used for modelling. On high-traffic accounts Advanced mode can recover 15-25% of conversions.

+ Does Consent Mode still apply to Universal Analytics?

No. Universal Analytics stopped processing data on July 1, 2024. Consent Mode v2 applies only to GA4 and Google Ads. UA-specific setups are no longer operational.

+ How is consent handled for Facebook Analytics?

Facebook Analytics was shut down on June 30, 2021. Consent on the Meta side now runs through Meta Pixel and Meta Conversions API (CAPI). fbq('consent', 'revoke') is called before the Pixel loads; when consent is granted, fbq('consent', 'grant') updates it.