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.
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.
Consent Mode v1 → v2: What Changed?
| Signal | v1 | v2 | Controls |
|---|---|---|---|
ad_storage | ✓ | ✓ | Read/write consent for advertising cookies |
analytics_storage | ✓ | ✓ | Consent for analytics cookies |
ad_user_data | — | ✓ | Whether user data (hashed email, ads click IDs) can be sent to Google |
ad_personalization | — | ✓ | Whether data can be used for remarketing and personalized ads |
functionality_storage | ✓ | ✓ | Cookies for site functionality |
personalization_storage | ✓ | ✓ | On-site personalization cookies |
security_storage | ✓ | ✓ | Security/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:
- Consent Mode’s measurement impact and basic vs. advanced mode comparison: You Added a Consent Banner and Traffic Dropped 30%.
- GA4 and Google Ads data controls consolidating under Consent Mode as a single gate in June 2026: GA4 and Google Ads Data Controls Consolidation.
- Bridging platform-specific Cookie Law with Consent Mode v2 on T-Soft stores: T-Soft Cookie Law and Google Consent Mode v2.
Installing Consent Mode v2 via gtag
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.
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.
region is only valid on the default command; it cannot be used on
update.
Example: Async gtag + Consent Tool Integration
<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
gcsparameter 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.
Google Tag Manager (GTM)
GTM offers two paths for Consent Mode v2: the pre-built Template Gallery or Custom HTML.
Template Gallery: Consent Mode (Google tags)
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.
Per-Tag Consent Settings
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_datarequired - Google Ads Remarketing:
ad_storage,ad_user_data,ad_personalizationrequired - GA4 Configuration:
analytics_storagerequired (built-in consent check) - Meta Pixel:
ad_storagerequired
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]).
Main cookies set by HubSpot:
| Cookie | Duration | Purpose |
|---|---|---|
__hs_opt_out | 13 months | Remembers the user’s opt-out choice |
__hs_do_not_track | 13 months | Runs the tracking code in anonymous mode |
__hs_initial_opt_in | 7 days | Prevents the banner from reappearing |
__hs_cookie_cat_pref | 13 months | Stores which cookie categories were accepted |
__hstc, hubspotutk, __hssc, __hssrc | Varies | Analytics 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(): returnstrueif 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():trueif 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.
sGTM: Server-Side Consent State
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.
BigQuery: GA4 Export and Consent State
The GA4 BigQuery export schema stores consent state in the privacy_info struct:
privacy_info.analytics_storage:Yes/No/Unsetprivacy_info.ads_storage:Yes/No/Unsetprivacy_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.
Related Posts
- You Added a Consent Banner and Traffic Dropped 30%: Measurement Impact of GDPR and KVKK
- GA4 and Google Ads Data Controls Consolidation: June 2026 Preparation Guide
- T-Soft Cookie Law and Google Consent Mode v2 Incompatibility
- Google Ads Enhanced Conversions Guide
Footnotes
- 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.
+ 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.