{"id":1579,"date":"2024-10-12T11:48:00","date_gmt":"2024-10-12T09:48:00","guid":{"rendered":"https:\/\/www.sabatka.net\/cs\/?p=1579"},"modified":"2026-10-12T11:50:48","modified_gmt":"2026-10-12T09:50:48","slug":"what-is-consent-mode-v2","status":"publish","type":"post","link":"https:\/\/www.sabatka.net\/en\/what-is-consent-mode-v2\/","title":{"rendered":"Google Consent Mode v2: What It Is and How It Works"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Your website has a cookie banner. A visitor clicks &#8220;Reject all&#8221;, the banner faithfully stores the choice, and you are convinced you&#8217;re done. Google Ads still reports missing consent signals.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The banner did nothing wrong. It just never told the tags, the pieces of code on your site that send data to tools like Google Analytics or Google Ads. The banner collects consent, but the tags on the page can&#8217;t read it by themselves. Google consent mode v2 is the link between the two: a way for the banner to tell the tags what they are allowed to do. And you can check whether it works in a few minutes, right in your browser.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button scroll_to_subscribe\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">Contact me<\/a><\/div>\n\n\n\n<div class=\"wp-block-button linkedinShare\"><a class=\"wp-block-button__link wp-element-button\">Share on LinkedIN<\/a><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">In this article I explain what consent mode is, why Google wants it, what the difference between Basic and Advanced is (and why &#8220;Advanced&#8221; does not mean &#8220;better&#8221;), and how to check the consent state in your browser&#8217;s developer tools (DevTools, the F12 key).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why we need Google consent mode v2: managing consent<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The banner is a doorman. Consent mode is the walkie-talkie the doorman uses to tell the rest of the building who he let in. Without the walkie-talkie, the doorman can do his job perfectly and the tags still won&#8217;t know a thing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To be precise: the banner (a CMP, short for consent management platform) collects and stores consent. Consent mode passes it to the tags so they can adjust their behavior. The tags then send data in requests, meaning the messages a browser sends to a server, such as Google&#8217;s. Consent mode itself has no banner and <a href=\"https:\/\/support.google.com\/google-ads\/answer\/10000067\" target=\"_blank\" rel=\"noopener\">does not collect consent<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"576\" data-src=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-flow-en-1024x576.png\" alt=\"\" class=\"wp-image-1599 lazyload\" data-srcset=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-flow-en-1024x576.png 1024w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-flow-en-300x169.png 300w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-flow-en-768x432.png 768w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-flow-en.png 1200w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/576;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Having consent mode in place does not mean GDPR compliance. Technically, it only passes consent on. I wrote up what most often goes wrong with the banner in <a href=\"https:\/\/www.sabatka.net\/en\/5-most-common-bugs-in-cookie-bar-configuration\/\">The 5 most common mistakes in cookie bar settings<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Consent types in consent mode<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Consent mode works with seven consent types. For four of them, Google documents how its tags change behavior accordingly; the other three you can use for your own tags. Each has a value of <code>granted<\/code> or <code>denied<\/code> (<a href=\"https:\/\/developers.google.com\/tag-platform\/security\/concepts\/consent-mode\" target=\"_blank\" rel=\"noopener\">documentation<\/a>). The v1 and v2 labels in the table are two generations of consent mode; I describe the difference below.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Type<\/th><th>What it is for<\/th><th>Who reads it<\/th><\/tr><\/thead><tbody><tr><td><code>ad_storage<\/code><\/td><td>storage (cookies, device IDs) for advertising<\/td><td>Google tags (since v1)<\/td><\/tr><tr><td><code>analytics_storage<\/code><\/td><td>storage for analytics, for example visit duration<\/td><td>Google tags (since v1)<\/td><\/tr><tr><td><code>ad_user_data<\/code><\/td><td>consent to send user data to Google for advertising<\/td><td>Google tags (since v2)<\/td><\/tr><tr><td><code>ad_personalization<\/code><\/td><td>consent to personalized advertising (remarketing)<\/td><td>Google tags (since v2)<\/td><\/tr><tr><td><code>functionality_storage<\/code><\/td><td>functional storage, for example language<\/td><td>your own use<\/td><\/tr><tr><td><code>personalization_storage<\/code><\/td><td>personalization, for example recommended videos<\/td><td>your own use<\/td><\/tr><tr><td><code>security_storage<\/code><\/td><td>security, authentication, fraud prevention<\/td><td>your own use<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The consent types form two logical pairs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>ad_storage<\/code> and <code>analytics_storage<\/code> determine what may be <strong>stored in the visitor&#8217;s browser<\/strong>. With <code>denied<\/code>, tags neither write nor read the corresponding cookies.<\/li>\n\n\n\n<li><code>ad_user_data<\/code> and <code>ad_personalization<\/code> determine what Google may <strong>do with the data<\/strong>. <code>ad_user_data<\/code> is consent for user data to be sent to Google for advertising purposes, for example for conversion measurement in Google Ads. <code>ad_personalization<\/code> is consent for using that data for personalized advertising, typically remarketing (showing ads to people who have already visited the site).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Each type is a separate signal, so the banner can, for example, allow analytics and deny advertising.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What Google requires<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Google requires consent signals for visitors from the European Economic Area (EEA), the United Kingdom and Switzerland, based on its <a href=\"https:\/\/www.google.com\/about\/company\/user-consent-policy\/\" target=\"_blank\" rel=\"noopener\">EU User Consent Policy<\/a>. For personalized advertising, both <code>ad_user_data<\/code> and <code>ad_personalization<\/code> must be <code>granted<\/code>. If <code>ad_personalization<\/code> is <code>denied<\/code>, remarketing in Google Ads receives no data. If <code>ad_user_data<\/code> is <code>denied<\/code>, the export of conversions by click ID (the identifier of a click on an ad) is limited (<a href=\"https:\/\/support.google.com\/google-ads\/answer\/13802165\" target=\"_blank\" rel=\"noopener\">Google Ads Help<\/a>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">From v1 to v2<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/blog.google\/products\/ads-commerce\/measure-conversions-while-respecting-user-consent-choices\/\" target=\"_blank\" rel=\"noopener\">The first version came out in September 2020<\/a> and had two consent types: <code>ad_storage<\/code> and <code>analytics_storage<\/code>. In November 2023, <code>ad_user_data<\/code> and <code>ad_personalization<\/code> were added. Google&#8217;s documentation justifies this by saying it is strengthening enforcement of its EU User Consent Policy (<a href=\"https:\/\/developers.google.com\/tag-platform\/security\/guides\/consent\" target=\"_blank\" rel=\"noopener\">gtag documentation<\/a>). So v2 is not a new product. It is two new signals.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>What changed in June 2026 (as of October 2026)<\/strong><br>From 15 June 2026, whether a Google Analytics 4 (GA4) tag collects advertising cookies and Google Ads identifiers is decided by consent mode alone (the consent setting for ads). Turning off Google Signals (a GA4 feature that links data to signed-in Google users) no longer stops it. Signals now only controls linking GA4 data to signed-in users for behavioral reports in GA4. Later in 2026, whether GA4 data is used to personalize ads in Ads is to be decided exclusively by <code>ad_personalization<\/code>; Google has not announced a date yet (<a href=\"https:\/\/support.google.com\/analytics\/answer\/17016975\" target=\"_blank\" rel=\"noopener\">Google Analytics Help<\/a>).<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">How to set up consent mode: default and update<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The principle has two steps. First you set the initial state (<code>default<\/code>), then you change it after the user decides (<code>update<\/code>). In practice, this is usually handled by the cookie banner, or by the tag through which you deploy the banner in Google Tag Manager (GTM), the tool for managing tags on a site. But it is worth checking that this is really the case.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is an adapted example based on the official guide for gtag.js (the JavaScript library behind the Google tag), <a href=\"https:\/\/developers.google.com\/tag-platform\/security\/guides\/consent\" target=\"_blank\" rel=\"noopener\">Set up consent mode on websites<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script&gt;\n  window.dataLayer = window.dataLayer || &#91;];\n  function gtag(){dataLayer.push(arguments);}\n\n  \/\/ Default state: everything is denied until the user decides.\n  \/\/ Must run BEFORE the Google tag loads, and on every page.\n  gtag('consent', 'default', {\n    'ad_storage': 'denied',\n    'ad_user_data': 'denied',\n    'ad_personalization': 'denied',\n    'analytics_storage': 'denied'\n  });\n&lt;\/script&gt;\n&lt;!-- The Google tag (gtag.js) loads only here --&gt;\n\n&lt;script&gt;\n  \/\/ The CMP calls this function from an event in the banner,\n  \/\/ at the moment the user accepts everything. It must NOT be\n  \/\/ pasted in as a loose script: that would grant everything\n  \/\/ to everyone right after the page loads.\n  function onUserAcceptsAll() {\n    gtag('consent', 'update', {\n      'ad_user_data': 'granted',\n      'ad_personalization': 'granted',\n      'ad_storage': 'granted',\n      'analytics_storage': 'granted'\n    });\n  }\n&lt;\/script&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Things to watch:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>default<\/code> must come before the Google tag and before any <code>config<\/code> or <code>event<\/code>, on every page. Google says it directly: &#8220;If your consent code is called out of order, consent defaults won&#8217;t work.&#8221;<\/li>\n\n\n\n<li><code>update<\/code> is called right after the user decides. The choice has to be remembered by the CMP, which passes it on again when the next page loads.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The example corresponds to Advanced mode: the Google tag loads right away (I explain the difference below). And if the default is already set by your CMP or its template in GTM, do not add the manual snippet, or the default values would be overwritten or duplicated.<\/p>\n\n\n\n<div class=\"wp-block-columns lab-note is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1024\" height=\"1024\" data-src=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know.png\" alt=\"V\u011bd\u011bli jste? \u2014 informa\u010dn\u00ed panel\" class=\"wp-image-936 lazyload\" data-srcset=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know.png 1024w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-300x300.png 300w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-150x150.png 150w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-768x768.png 768w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/1024;\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<h3 class=\"wp-block-heading\"><strong><strong>Lab note<\/strong><\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The default <code>denied<\/code> works like a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Null_hypothesis\" target=\"_blank\" rel=\"noopener\">null hypothesis<\/a> in statistics: until you have evidence to the contrary, the starting assumption holds. Here it reads &#8220;the visitor does not consent&#8221;. The evidence is the click in the banner, and <code>update<\/code> is the evaluation of the test: it either rejects the starting assumption or confirms it.<\/p>\n<\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Besides the consent types, there are a few parameters that influence how tags behave:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>wait_for_update<\/code> is the time in milliseconds that tags wait for an asynchronous CMP before they send a hit (a request carrying data).<\/li>\n\n\n\n<li><code>region<\/code> limits the default setting to selected countries or regions (ISO 3166-2 codes, for example <code>CZ<\/code> or <code>DE-BY<\/code>). The setting with the most specific region is applied. Without <code>region<\/code>, the default applies to all visitors.<\/li>\n\n\n\n<li><code>url_passthrough<\/code>: when cookies are denied, it carries ad click information, for example the GCLID (the Google Ads click identifier), between pages of the same site in link URLs, so that it is not lost along the way. It applies to Google Ads and Floodlight tags (the measurement tag of Google Marketing Platform).<\/li>\n\n\n\n<li><code>ads_data_redaction<\/code>: when <code>ad_storage='denied'<\/code>, it does the opposite and removes click identifiers from requests sent by Google Ads and Floodlight tags. You will find details on both in the <a href=\"https:\/\/developers.google.com\/tag-platform\/security\/guides\/consent\" target=\"_blank\" rel=\"noopener\">documentation<\/a>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Consent mode in GTM<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In GTM, you should fire CMP tags on the Consent Initialization &#8211; All Pages trigger, which fires before all other tags, including before the Initialization trigger (<a href=\"https:\/\/support.google.com\/tagmanager\/answer\/10718549\" target=\"_blank\" rel=\"noopener\">GTM Help<\/a>). CMP templates from the Community Template Gallery use the <code>setDefaultConsentState<\/code> and <code>updateConsentState<\/code> APIs, so you should not call <code>gtag('consent', \u2026)<\/code> manually in them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Consent mode Basic vs Advanced: Advanced does not automatically mean better<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Google distinguishes two modes in which consent mode works: Basic and Advanced. Both use the same consent types (for example <code>analytics_storage<\/code>) and pass them in requests with the same parameters. They differ in when the tags load and what leaves them until the user consents. This choice is easy to overlook during deployment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Three terms I will need from here on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Cookieless ping<\/strong> is measurement without cookies: the tag sends Google a message that a visit or event happened, but without a user identifier.<\/li>\n\n\n\n<li><strong>Modeling<\/strong> means that Google fills in the missing data from users who declined with a statistical estimate.<\/li>\n\n\n\n<li><strong>BigQuery<\/strong> is Google&#8217;s database, where GA4 can export raw data for individual events.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><\/th><th>Basic<\/th><th>Advanced<\/th><\/tr><\/thead><tbody><tr><td>Loading of Google tags<\/td><td>only after interaction with the banner<\/td><td>immediately when the page opens<\/td><\/tr><tr><td>What is sent when consent is denied<\/td><td>nothing, not even the consent state<\/td><td>cookieless pings<\/td><\/tr><tr><td>Modeling in GA4<\/td><td>none<\/td><td>possible, but only above thresholds<\/td><\/tr><tr><td>Conversion modeling in Google Ads<\/td><td>general model<\/td><td>advertiser-specific model<\/td><\/tr><tr><td>Users who declined in BigQuery<\/td><td>missing<\/td><td>events without a persistent ID<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Basic: nothing leaves from declined visits<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In Basic mode, Google tags do not load until the user clicks in the banner. If they decline, nothing is sent to Google, not even the consent state. The price: in GA4 you don&#8217;t get the modeled data that fills the gaps (<a href=\"https:\/\/support.google.com\/analytics\/answer\/9976101\" target=\"_blank\" rel=\"noopener\">GA4 Help<\/a>). In Google Ads, conversion modeling relies only on the general model. And users who declined are missing from the BigQuery export too, because nothing about them reached Google.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Advanced: cookieless pings and what they bring<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In Advanced mode, tags load immediately and the default state is <code>denied<\/code>. Until the user consents, they send cookieless pings. According to Google&#8217;s help, these may contain:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>a timestamp,<\/li>\n\n\n\n<li>the user agent (the type of browser and operating system),<\/li>\n\n\n\n<li>the referrer (the page the visitor came from),<\/li>\n\n\n\n<li>the screen resolution,<\/li>\n\n\n\n<li>the IP address (part of normal communication),<\/li>\n\n\n\n<li>the consent state,<\/li>\n\n\n\n<li>a random number generated on every page load.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Without <code>ads_data_redaction<\/code>, with <code>ad_storage='denied'<\/code> the full page URL is also sent, which may contain the GCLID parameter, see <a href=\"https:\/\/support.google.com\/analytics\/answer\/9976101\" target=\"_blank\" rel=\"noopener\">GA4 Help<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Advanced mode you get:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Conversion modeling in Google Ads. It has minimum data volume requirements, though; the status is shown by conversion diagnostics in Google Ads.<\/li>\n\n\n\n<li>Behavioral modeling in GA4. For it to start working, you need at least 1,000 events per day with <code>analytics_storage='denied'<\/code> for 7 days, and at least 1,000 daily users with <code>granted<\/code> on at least 7 of the last 28 days. If you are only slightly above these limits, it does not automatically mean modeling will be available (<a href=\"https:\/\/support.google.com\/analytics\/answer\/11161109\" target=\"_blank\" rel=\"noopener\">GA4 Help<\/a>).<\/li>\n\n\n\n<li>Cookieless events in BigQuery, that is, raw data without a persistent user identifier (so without any of the modeling mentioned above). Advanced therefore does not add the missing users to BigQuery. It delivers event counts that cannot be tied to users or sessions.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">The risk: data leaves before consent<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Cookies are not used in Advanced mode, but the browser still hands over technical data with the ping. This is primarily covered by Article 5(3) of the ePrivacy Directive (the directive on privacy in electronic communications), the rule on storing and reading data on a user&#8217;s device. The Czech implementation, Section 89(3) of the Electronic Communications Act, is covered in my article <a href=\"https:\/\/www.sabatka.net\/en\/gdpr-cookie-bars-finaly-in-czechia\/\">Cookie bars finally in Czechia<\/a>. The European Data Protection Board (EDPB) reads Art. 5(3) in its <a href=\"https:\/\/www.edpb.europa.eu\/documents\/guideline\/guidelines-22023-on-technical-scope-of-art-53-of-eprivacy-directive_en\" target=\"_blank\" rel=\"noopener\">Guidelines 2\/2023<\/a> more broadly than just cookies. According to it, this also includes the situation where JavaScript on the page instructs the browser to send data to a server, and it can concern the IP address or user agent if sending them is triggered by such code. If this interpretation applies, even sending a cookieless ping may require consent, which is exactly what Advanced does before any click in the banner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Google states that GA4 does not store the IP addresses of visitors from the EU, Switzerland and the United Kingdom and discards them immediately after determining geolocation (<a href=\"https:\/\/support.google.com\/analytics\/answer\/12017362\" target=\"_blank\" rel=\"noopener\">GA4 Help<\/a>). The risk is therefore mainly that the transfer happens before consent. I did not find an explicit statement from a supervisory authority on Google consent mode.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Simo Ahava recommends Basic as the default choice and Advanced only after careful consideration with a legal team. The broader question of whether Google tags may load before consent at all I cover in the article on the Hannover ruling, <a href=\"https:\/\/www.sabatka.net\/en\/gtm-gdpr-conflict\/\">GTM in violation of the GDPR<\/a>. How the rules may evolve further I describe in the text on the <a href=\"https:\/\/www.sabatka.net\/en\/digital-omnibus-in-digital-analytics\/\">Digital Omnibus<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How to decide<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">My take: if a site does not advertise in Google Ads to visitors from the EEA and does not meet the GA4 thresholds, Advanced will mainly bring risk. If it does advertise, the advertiser-specific conversion model can have real value (provided the account meets the data volume requirements), and that has to be weighed against the risk together with a lawyer or a data protection officer (DPO).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And once more: keep in mind that Advanced does not automatically mean better.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What it looks like technically: the gcs and gcd parameters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The requests that a Google tag sends contain parameters that tell Google the consent state. Google&#8217;s documentation mentions <code>gcs<\/code> and <code>gcd<\/code>, among others, but does not document their format and says it may change. For debugging, it recommends Tag Assistant, its debugging tool for tags. On top of that, the <code>gcd<\/code> parameter is always sent, even if you don&#8217;t activate consent mode. Its presence therefore proves nothing.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Heads up: the decoding below is not official.<\/strong> The meaning of <code>gcs<\/code> and <code>gcd<\/code> values comes from community reverse engineering (notably Simo Ahava and CMP vendors). Google does not publish it and can change it at any time. Treat it as an orientation aid, not a specification.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">gcs: two signals<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>gcs<\/code> parameter knows only two signals, <code>ad_storage<\/code> and <code>analytics_storage<\/code>. It has the form <code>G1xy<\/code>, where x is <code>ad_storage<\/code> and y is <code>analytics_storage<\/code>. One means <code>granted<\/code>, zero means <code>denied<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>gcs<\/th><th>ad_storage<\/th><th>analytics_storage<\/th><\/tr><\/thead><tbody><tr><td><code>G100<\/code><\/td><td>denied<\/td><td>denied<\/td><\/tr><tr><td><code>G110<\/code><\/td><td>granted<\/td><td>denied<\/td><\/tr><tr><td><code>G101<\/code><\/td><td>denied<\/td><td>granted<\/td><\/tr><tr><td><code>G111<\/code><\/td><td>granted<\/td><td>granted<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You will see <code>G100<\/code> only in Advanced. In Basic, nothing is sent after a decline, so there is nothing to read.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">gcd: four signals and the difference between default and update<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>gcs<\/code> knows only two signals. The consent mode gcd parameter arrived with v2 and carries all four, including the distinction between default and update. Again, this is a community observation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The letters in <code>gcd<\/code> go in the order <code>ad_storage<\/code>, <code>analytics_storage<\/code>, <code>ad_user_data<\/code>, <code>ad_personalization<\/code>. Each letter encodes a pair: the default state and the possible update. I do not interpret the digits between the letters. Nobody has reliably documented their meaning, and they differ between sites; for reading consent, the letters are enough.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>13r3r3r3r5<\/code>: four times <code>r<\/code>, meaning default <code>denied<\/code>, update <code>granted<\/code>. The user accepted everything.<\/li>\n\n\n\n<li><code>13l3l3l2l2<\/code>: all <code>l<\/code>. Consent mode was not found, it is inactive.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">And here is the cheat sheet for the individual letters:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Letter<\/th><th>Default<\/th><th>Update<\/th><\/tr><\/thead><tbody><tr><td><code>l<\/code><\/td><td>not set<\/td><td>not set (consent mode not found)<\/td><\/tr><tr><td><code>p<\/code><\/td><td>denied<\/td><td>no update<\/td><\/tr><tr><td><code>q<\/code><\/td><td>denied<\/td><td>denied<\/td><\/tr><tr><td><code>r<\/code><\/td><td>denied<\/td><td>granted<\/td><\/tr><tr><td><code>t<\/code><\/td><td>granted<\/td><td>no update<\/td><\/tr><tr><td><code>u<\/code><\/td><td>granted<\/td><td>denied<\/td><\/tr><tr><td><code>v<\/code><\/td><td>granted<\/td><td>granted<\/td><\/tr><tr><td><code>m<\/code><\/td><td>not set<\/td><td>denied<\/td><\/tr><tr><td><code>n<\/code><\/td><td>not set<\/td><td>granted<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The table shows that before a click in the banner (default denied, no update) you will see <code>p<\/code>, after declining <code>q<\/code>, after accepting <code>r<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How to verify it<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Start with the simplest route: <a href=\"https:\/\/tagassistant.google.com\/\" target=\"_blank\" rel=\"noopener\">Tag Assistant<\/a>, which Google itself recommends for debugging. If you want to see the raw request:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open the site in an incognito window. The CMP remembers the choice, so otherwise you would be testing an already stored state.<\/li>\n\n\n\n<li>Open DevTools and the Network tab. Type <code>collect<\/code> into the filter.<\/li>\n\n\n\n<li>Click decline or accept in the banner and find the <code>gcs<\/code> and <code>gcd<\/code> parameters in the GA4 requests. In Basic you will see no GA4 request after declining, and that is fine.<\/li>\n\n\n\n<li>Check that the letters in <code>gcd<\/code> are not all <code>l<\/code>. The mere presence of <code>gcd<\/code> means nothing.<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"597\" data-src=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-parameters-in-dev-tools-2-1024x597.png\" alt=\"\" class=\"wp-image-1609 lazyload\" data-srcset=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-parameters-in-dev-tools-2-1024x597.png 1024w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-parameters-in-dev-tools-2-300x175.png 300w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-parameters-in-dev-tools-2-768x448.png 768w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-parameters-in-dev-tools-2-1536x896.png 1536w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/10\/consent-mode-parameters-in-dev-tools-2.png 1918w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/597;\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Google is not alone: consent signals on other platforms<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every platform needs a way to pass consent to its tags. The principle is similar everywhere: the consent signal must be set before data is sent. The syntax differs.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Platform<\/th><th>How the signal is passed<\/th><th>What to remember<\/th><th>Documentation<\/th><\/tr><\/thead><tbody><tr><td>Microsoft Advertising (UET, Universal Event Tracking)<\/td><td>consent mode with a single signal, <code>ad_storage<\/code><\/td><td>Mandatory for the EEA, UK and Switzerland from 5 May 2025. Without a default set, UET assumes <code>denied<\/code> in the EEA, UK and Switzerland, and <code>granted<\/code> elsewhere.<\/td><td><a href=\"https:\/\/about.ads.microsoft.com\/en\/blog\/post\/march-2025\/providing-user-consent-signals-on-your-microsoft-campaigns-by-may-5-2025\" target=\"_blank\" rel=\"noopener\">Microsoft blog<\/a>, <a href=\"https:\/\/learn.microsoft.com\/en-us\/advertising\/msa-help\/hlp_ba_conc_uet_consent\" target=\"_blank\" rel=\"noopener\">UET help<\/a><\/td><\/tr><tr><td>Microsoft Clarity<\/td><td><code>clarity('consentv2', { ad_Storage: \u2026, analytics_Storage: \u2026 })<\/code><\/td><td>Mind the capital S. Enforced for the EEA, UK and Switzerland from 31 October 2025; without consent it runs without cookies.<\/td><td><a href=\"https:\/\/learn.microsoft.com\/en-us\/clarity\/setup-and-installation\/clarity-consent-api-v2\" target=\"_blank\" rel=\"noopener\">Consent API v2<\/a><\/td><\/tr><tr><td>Meta pixel<\/td><td><code>fbq('consent', 'revoke')<\/code> before <code>fbq('init', \u2026)<\/code>, after consent <code>fbq('consent', 'grant')<\/code><\/td><td>The call belongs on every page.<\/td><td><a href=\"https:\/\/developers.facebook.com\/docs\/meta-pixel\/implementation\/gdpr\" target=\"_blank\" rel=\"noopener\">Meta documentation<\/a><\/td><\/tr><tr><td>Pinterest<\/td><td><code>pintrk('setconsent', true)<\/code> or <code>false<\/code><\/td><td>With <code>false<\/code>, events are not sent.<\/td><td><a href=\"https:\/\/help.pinterest.com\/en\/business\/article\/install-the-base-code\" target=\"_blank\" rel=\"noopener\">Pinterest<\/a><\/td><\/tr><tr><td>Amazon Ads<\/td><td>Amazon Consent Signal (ACS), IAB TCF (Transparency and Consent Framework, an industry standard for passing consent) or GPP (Global Privacy Platform)<\/td><td>One of the three is enough; when several signals are present, TCF wins.<\/td><td><a href=\"https:\/\/advertising.amazon.com\/resources\/ad-policy\/consent-signal-requirements\" target=\"_blank\" rel=\"noopener\">Amazon Ads<\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The TikTok pixel has its own mode, see <a href=\"https:\/\/ads.tiktok.com\/help\/article\/how-to-use-cookies-with-tiktok-pixel\" target=\"_blank\" rel=\"noopener\">TikTok Ads Help<\/a>. I described the consent setup for Clarity separately: <a href=\"https:\/\/www.sabatka.net\/en\/consent-mode-v2-for-ms-clarity\/\">Consent for MS Clarity<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Is consent mode the same as a cookie banner?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. The banner collects consent, consent mode passes it to the tags (see above). Without a banner, consent mode has nothing to pass on.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is consent mode v2 mandatory?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Google requires consent signals for visitors from the EEA, the United Kingdom and Switzerland (EU User Consent Policy). It is a Google requirement, not a law. If you want to use personalization, remarketing or conversion measurement for these visitors, you need the signals. Google requires a certified CMP from AdSense, Ad Manager and AdMob publishers; advertisers have separate rules (<a href=\"https:\/\/support.google.com\/adsense\/answer\/13554116\" target=\"_blank\" rel=\"noopener\">AdSense Help<\/a>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why did data in GA4 drop after deployment?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Only those who consented are measured, so a drop is expected. What remains from those who declined depends on Basic vs Advanced (see above). You can work out your share of consents from the article <a href=\"https:\/\/www.sabatka.net\/en\/what-is-opt-in-rate\/\">How to calculate the opt-in rate of cookie bars<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What do G100 and G111 mean?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">These are values of the <code>gcs<\/code> parameter. <code>G100<\/code> means both <code>ad_storage<\/code> and <code>analytics_storage<\/code> are <code>denied<\/code>, and you will see it only in Advanced. <code>G111<\/code> means both are <code>granted<\/code>. The decoding is community-based; Google does not document the format.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Consent mode is not a cookie banner. It is the way the banner tells the tags what they are allowed to do. The basics are simple: a default state before the tags, an update after the decision, and a check of <code>gcs<\/code> and <code>gcd<\/code> in the request. The harder part is the choice between Basic and Advanced, which does not belong to the analyst alone.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button scroll_to_subscribe\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">Contact me<\/a><\/div>\n\n\n\n<div class=\"wp-block-button linkedinShare\"><a class=\"wp-block-button__link wp-element-button\">Share on LinkedIN<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Your website has a cookie banner. A visitor clicks &#8220;Reject all&#8221;, the banner faithfully stores the choice, and you are convinced you&#8217;re done. Google Ads still reports missing consent signals. The banner did nothing wrong. It just never told the tags, the pieces of code on your site that send data to tools like Google [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1585,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,10],"tags":[],"class_list":["post-1579","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-digital-analytics","category-google-tag-manager-gtm"],"_links":{"self":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1579","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/comments?post=1579"}],"version-history":[{"count":12,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1579\/revisions"}],"predecessor-version":[{"id":1610,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1579\/revisions\/1610"}],"wp:attachment":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/media?parent=1579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/categories?post=1579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/tags?post=1579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}