Introduction

Google Consent Mode V2 (let’s call it CoMo2) is a change to how Google wants to deal with consent in the Google Tag (gtag) and Google Tag Manager (GTM). In March 2024, Google will turn off some advertising features in the European Economic Area (EEA) when they are not explicitly informed that a user has provided consent.

I’m not a lawyer or privacy expert. This article does not contain any legal advice or instructions on how you should implement CoMo2 on your website. This is just me documenting what I learnt as I implemented CoMo2 and consent aware tracking for our Tag Rocket app on BigCommerce.

Please consult your legal counsel or Data Protection Officer (DPO) on what is right to do in your scenario. If they disagree with my conclusions, please tell me about it!

And thanks to Simo Ahava for his articles and for answering all my (and others) questions.

Why March 2024?

On the 6th March 2024 the Digital Markets Act (DMA) comes into force in the EEA. CoMo2 is Google’s move to try and be compliant with the act.

The European Economic Area (EEA) is the EU plus Iceland, Liechtenstein and Norway. It does not include the UK. CoMo2 requirement relate to the EEA, so does not include the UK at this time (They are working on their own bill).

The DMA applies to the Gatekeepers, which are currently Alphabet, Amazon, Apple, ByteDance, Meta, Microsoft and some of the platforms they provide. For Google/Alphabet, it covers Google Search, Shopping, Ads, Chrome, Android, Play, Maps and Youtube.

DMA is about making these Gatekeepers “behave in a fair way online”. The consequences of non-compliance can be fines, penalties and more.

DMA covers a lot of things. We’re just focused on website consent. It should be apparent from the following snippets that in the EEA and UK, the user has to opt-in. That is, they have to explicitly give consent before you can use their Personally Identifiable Information (PII). They should be given equal opportunity to deny or grant consent, and it should be easy to change their mind later. This is unlike the US regulations (Legislation Tracker), which are opt-out, i.e. you can track users unless they explicitly say no.

Bolding in the quotes is added by me to emphasise specific phrases.

source

gatekeepers should enable end users to freely choose to opt-in to such data processing and sign-in practices by offering a less personalised but equivalent alternative, and without making the use of the core platform service or certain functionalities thereof conditional upon the end user’s consent.

DMA – 36

The new ad_personalization consent parameter might relate to the above.

Not giving consent should not be more difficult than giving consent. When the gatekeeper requests consent, it should proactively present a user-friendly solution to the end user to provide, modify or withdraw consent in an explicit, clear and straightforward manner. In particular, consent should be given by a clear affirmative action or statement establishing a freely given, specific, informed and unambiguous indication of agreement by the end user, as defined in Regulation (EU) 2016/679.

Lastly, it should be as easy to withdraw consent as to give it.

DMA – 37

Regulation (EU) 2016/679, aka GDPR (General Data Protection Regulation), has been around for a while and is mentioned quite a few times in the DMA. It defines a lot of rules on how to acquire consent. (The UK has The Data Protection Act (DPA), which is very similar to the GDPR).

Consent should be given by a clear affirmative act establishing a freely given, specific, informed and unambiguous indication of the data subject’s agreement to the processing of personal data relating to him or her, such as by a written statement, including by electronic means, or an oral statement. This could include ticking a box when visiting an internet website, choosing technical settings for information society services or another statement or conduct which clearly indicates in this context the data subject’s acceptance of the proposed processing of his or her personal data. Silence, pre-ticked boxes or inactivity should not therefore constitute consent. Consent should cover all processing activities carried out for the same purpose or purposes. When the processing has multiple purposes, consent should be given for all of them. If the data subject’s consent is to be given following a request by electronic means, the request must be clear, concise and not unnecessarily disruptive to the use of the service for which it is provided.

GDPR – 32

The mention of “purposes” in 32 may be why CoMo2 has added two new consent options related to how the user’s data will be used.

The following two quotes seem to talk about Google needing to demonstrate that the users they are tracking have provided consent. This may be why CoMo2 requires you to send explicit consent data for users in the EEA. I also think this may be a way to pass the responsibility of recording accurate consent to the website owner or the Consent Management Platform (CMP) they use.

Where processing is based on the data subject’s consent, the controller should be able to demonstrate that the data subject has given consent to the processing operation.

GDPR – 42

Where processing is based on consent, the controller shall be able to demonstrate that the data subject has consented to processing of his or her personal data.

GDPR – Article 7 – 1

The ePrivacy Directive (EPD) overlaps with the GDPR and applies to the EU. I’m unsure if it adds much to what’s concerning here? Google has been fined for violating the EPD in 2022.

And finally, some from Google themselves:

“To keep using measurement, ad personalization, and remarketing features, you must collect consent for use of personal data from end users based in the EEA and share consent signals with Google. The requirements also apply if you are using Google Analytics data with a Google service.”

“If a user from the EEA is using your website or app and you measure user behavior with Google tags, you need to pass through end-user consent choices to Google. Consent mode allows you to adjust how your Google tags behave based on the visitor’s interaction with the consent banner on your website.”

Updates to consent mode for traffic in European Economic Area (EEA)

Google’s EU user consent policy also covers the UK with regard to collecting user consent. In January 2024, Google Ads started to email advertisers saying that their consent banner was not compliant, with the potential to be suspended (more info).

End note: When I see a consent banner, I have a personal policy of specifically denying consent if a banner is non-compliant. In this manner, I punish non-compliant banners and reward compliance 🙂.

What is CoMo2?

I’m going to focus on the gtag implementation of CoMo2. Google Tag Manager also has a well-integrated consent system that wraps around the gtag solution, which I cover towards the end.

Google has listed Consent Management Platforms (CMP) that have integrated with Google Tag Manager (GTM) and Consent Mode. This will make it easier to set up CoMo2.

In the beginning, there was no mechanism to tell Google about consent. You needed to decide what you sent to Google based on the consent information you gathered. e.g. you would only send GA4 events if the user had granted that permission. You were effectively blocking all communication until consent was granted.

Then, they introduced CoMo1 as an option. You either carried on as before by blocking the sending of anything. Or you could set consent states and then send all the events. An implementation would set the default consent states before any events on a page. It would be perfect if you knew the correct states then. e.g.

gtag("consent", "default", {
  functionality_storage: "granted", 
  analytics_storage: "granted",
  ad_storage: "denied"
});

CoMo2 added two new consent options. ad_user_data and ad_personalization.

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

Make sure you set them to the correct ‘granted’ text if it is granted. Any other text is considered to be denied.

Consent TypeDescription
ad_storageEnables storage, such as cookies (web) or device identifiers (apps), related to advertising. It does not disable gclid/dclid. It does truncate the IP address. This also covers other ad platforms like Facebook, Microsoft, Pinterest etc.
ad_user_dataSets consent for sending user data to Google for online advertising purposes. Limits the use of Enhanced Conversion data.
ad_personalizationSets consent for personalized advertising. This includes audiences and remarketing.
analytics_storageEnables storage, such as cookies (web) or device identifiers (apps), related to analytics, for example, visit duration. GA4 and other analytics tools.
functionality_storageEnables storage that supports the functionality of the website or app, for example, language settings
personalization_storageEnables storage related to personalization, for example, video recommendations
security_storageEnables storage related to security such as authentication functionality, fraud prevention, and other user protection
Consent types

You can invent your own consent types for your own use.

BigCommerce stores its consent states in a cookie. Tag Rocket uses that to send the correct default states before it sends any events.

However, some consent systems may need time to gather the current consent states. e.g. If a JavaScript file needs to be loaded. In these scenarios, the default would typically be set to all denied, and the correct states would be sent in a later update. To stop events going out with the wrong consent states, you should tell gtag to wait a while before it starts sending the events it received (wait_for_update). This gives your system time to set the correct states:

gtag("consent", "default", {
  functionality_storage: "denied", 
  analytics_storage: "denied",
  ad_storage: "denied",
  ad_user_data : "denied",
  ad_personalization: "denied",
  wait_for_update: 2000 // milliseconds
});

When the system does have the data, it would update the consent states, hopefully within the allocated time.

gtag("consent", "update", {
  functionality_storage: "granted",
  analytics_storage: "granted",
  ad_storage: "denied",
  ad_user_data : "denied",
  ad_personalization: "denied"
});

The consent update command is also useful when users change their consent settings. Using the command then means all subsequent events on the page are sent with the new states. This can be handy to help avoid losing attribution data like the landing page referrer.

If there are multiple default or update calls, the update settings take precedence. For example, a default after an update will not alter what the update set.

At the end of January, Google sent out emails to clarify how the new options would work. If ad_user_data is not set, it will default to the ad_storage setting. However, this will not happen to ad_personalization:

For our solution, I decided not to use the wait_for_update parameter. Tag Rocket manages more than just Google tags, and I wanted all the tags to wait until the true consent was established. So, I developed our version of the wait for update that all our tags support. Each tag waits for consent to be established before it creates and sends commands.

With the DMA, Google decided that in the EEA, it would need to be explicitly told about what state the consent was in for every event sent to Google. So, for users in the EEA, the option not to set consent and block was dropped.

Instead, they introduced two modes: Basic and Advanced.

Basic and Advanced modes

Here’s Google’s definition of the two modes:

FeatureBasic consent modeAdvanced consent mode
Tag loadingBlocked until user interaction with a consent banner.Loads with defaults set to denied, unless configured otherwise.
Data transmissionNo data is sent before a user consents – not even the default consent status.When consent is denied, consent state and cookieless pings are sent.
When consent is granted, cookies are written and all measurement data is sent.
Consent statesSet after user interaction.Defaults set to denied, unless configured otherwise; updates based on user choice.
Tag behavior after user interactionLoads and executes consent mode APIs only when a user grants consent.Adjusts tag behavior based on user consent choice.
Conversion modelingGeneral model (less detailed modeling).Advertiser-specific model (more detailed modeling).
Consent Mode Overview

Advanced is the same as CoMo1. You always send the events after the consent states have been set.

Basic is a slight change on the old blocking method. You only send events once you have consent, but when you do, you explicitly set the consent states. Plus, you only send events that have been given consent. That way, Google is always explicitly told what the current consent states are.

Tag Rocket supports both modes. Ironically, it was a lot harder to implement the basic mode. There are some concerns about the validity of Advanced mode, so Tag Rocket defaults to Basic.

In Advanced mode, you must set the valid consent states before events start flowing out.

With GA4, if the events go out as analytics_storage denied, and later on that page analytics_storage is granted, then GA4 will reprocess those denied events to be granted (more info).

In Basic mode, the main complication is that gtag supports sending events to multiple destinations with different consent requirements: Analytics (analytics_storage) or Ads (ad_storage).

Tag Rocket does not request the gtag.js until one of the consent requirements is met. So, there is no communication until the user has granted some consent.

We then must be careful only to send events with the required consent. e.g. if they granted analytics_storage but not ad_storage, then ads events are not sent.

Our solution was to create two command queues for Analytics and Ads. These were used if their relevant consent had not yet been granted. e.g. Ads events went to the Ads queue if ad_storage was denied. Once consent was granted, the queued items were sent to gtag.

Some commands bypassed the queues, like the ‘consent’ commands and some ‘set’ commands. However, ‘config’ and ‘event’ commands are queued up.

This not only stops us breaking the basic mode rules but also means we do not lose any events on the page where the user granted consent.

No Consent Banner!

Quite a few websites around the world do not have consent banners. Maybe the laws where their users are from do not require asking for consent.

Currently, we do not use Consent Mode in these cases.

If you do not set consent states, you leave it up to Google to decide what to do. In the EEA with opt-in rules, they will probably assume no consent is given. While in other countries with opt-out rules, they may assume consent is granted. This hopefully keeps the risk on Google’s side while automatically letting you track in regions Google deems are opt-out (Like the US). We will see.

I’m also concerned about telling Google a user has granted consent when they have not. This is Google’s record to keep them compliant and is meant to be for explicit consent, not implicit.

The CMPs I’ve checked set implicit consent states when they don’t show a consent banner. Maybe I’m wrong? We will find out more at the start of March.

An extreme thought that seems to match better with the regulation is not to set consent states until the user explicitly provides their consent preferences.

Google Ads

My observations indicate that in Advanced mode, only an initial ping is sent, and no other events are sent apart from a simplified conversion event. Once ad_storage is granted, it will send all the events, as I do with my Basic mode Ads queues.

The Ads Data Redaction and URL Passthrough settings only apply to Advanced mode, with their options not documented for Basic mode (reference).

GA4

GA4 in Advanced mode sends all events containing the current consent states. So, getting your correct consent states set before the events start to flow is essential.

Google has a Consent settings report in the GA4 stream that indicates if consent signals are being received. A good way to confirm if things are set up correctly.

I believe GA4 itself only needs analytics_storage to function. But I’d still send it all the consent states as it can be linked to Google Ads, and GA4 would need to know if it can pass data on. You can control which Google services can receive consented data from the Google tag via the last option in the above screenshot:

Enhanced Conversions

Enhanced Conversions are where user information, such as their email address, is sent to Google in a hashed form. Google may use this on conversions to try to match the user with a Google account so they can more accurately identify who caused the conversion. It can help Google identify what adverts a Google user saw before converting.

Note that hashing Personally Identifiable Information (PII) does not mean you are not sharing it. Google can compare this hashed data with hashes of its data to determine matches, and they have a lot of data. Do you have a Google account? The other big players also play the hashing of PII game.

Enhanced Conversion data is often available to send if a user has logged in or filled in a form. I believe you don’t even need to submit the form for these systems to grab it.

For Google Ads, Enhanced Conversion data is only sent on the ‘purchase’ event (Probably also the ‘conversion’ event). And it is not sent if ad_user_data is denied.

For Google Ads, you can enable Enhanced Conversions from within a conversions editor.

There are multiple ways to gather the Enhanced Conversion data. We explicitly provide Google with the data to avoid mistakes (gtag hashes it before sending):

gtag('set', 'user_data', {
  email: '[email protected]',
  phone_number: '01234567890',
  first_name: 'John',
  last_name: 'Doe',
  address: {
    street: '1 My Street',   
    city: 'Adelaide',
    region: 'South Australia',
    postal_code: '5000',
    country: 'AU'   
  }
});

You can provide the user_id when you config your GA4 tag. We set it to the logged-in customer ID when available:

gtag('config', 'GA4_MEASUREMENT_ID', {user_id: 'CUSTOMER_ID'});

Google Tag Manager (GTM)

As mentioned, our solution uses gtag, so we don’t play too much with GTM.

GTM supports consent and Consent Mode. Many CMPs have implemented GTM Consent Mode templates to help integrate them with the GTM consent system.

I discovered that setting consent states in our non-GTM gtag also updates the state in GTM. However, there is a timing issue. If the GTM code is before your gtag consent code, it will trigger tags on “All pages” before you set consent. That’s where this sneaky little trick works for us. Our code is inline, running before the GTM script is loaded. I use this function for the commands (consent & set) that must be added before the gtm.js event, which triggers “All pages” tags.

window.gtagunshift=function(){dataLayer.unshift(arguments)}; 

If you deny all consent via gtag, make sure your code is before the GTM code, or use my trick. Please don’t set the default inside an external script; it will be too late. Cookiebot also worked this one out: 

Advanced consent mode requires no additional work for the Google tags once you are sure the consent states are correctly set. The Google tags always fire with the consent states sent with them.

For Basic mode, you must block the tags from firing if the relevant consents are not granted. This is also the case for non-Google tags that don’t support a consent mode.

In a tags Advanced Settings, there is a Consent Settings section where you must add the consent required for that tag.

You want to add this to all tags requiring a form of consent; this includes the Config tags, which will trigger the loading of the gtag script when they have consent. You don’t want that script loading without consent.

If you don’t set consent states at all, then all tags fire, even those with consent restrictions added. So make sure you always set default consents if you require consent.

GTM does not automatically enable scripts or send events when a user grants consent. Unlike our solution, which queues up the events and dynamically adds scripts as soon as they have permission. For GTM, a page change is required before tracking starts, and with that, the loss of attribution data. It would be nice to see a simple solution for this (I’ve seen complex ones).

CMP Issues

While implementing our solution I came upon several issues when working with CMPs.

We automatically were setting the default states to denied, assuming the CMP would update them. One gotcha was that a client decided the CMP was not needed on the conversion page. This meant consent was denied on that page even if they had granted it previously, and conversions were lost. The solution was to track the CMP’s consent in a localStorage variable and set the default states to that, enabling Consent Mode to continue to be supported even when the CMP was not on a page.

I found out that our dataLayer monitoring did not pick up consent changes made within GTM. Many CMPs have GTM Template based solutions, so we were missing some consent changes. To solve that I created our own GTM Template that listens for consent changes and tells our app about them.

We frequently found broken CMP implementations. Please test that the banner shows in the regions you want it to show. If it’s shown or not, ensure it sets the correct consent states. A few clients don’t show a banner in Australia, but the CMP defaults to all denied!

And we already have 4 clients that checked the CMP box, but were not using a CMP!

Beyond Google

Don’t forget to ensure all your tags that require consent are complying with the users choices.

Facebook, Microsoft, and Pinterest also have their own consent modes. Microsoft’s solution is similar to Google’s. Facebook just delays sending anything until consent is granted. Pinterest looks like Facebook, but watch out: If you default to denied, it will erase its cookies.

Tools

Google Tag Assistant tracks all your gtag and GTM events and the consent state when each event fires. A great tool to make sure consent states are correctly set before events go out.

Note that it does change the behaviour of the code it is debugging. For example, it switches to GA4 batch processing. I saw load events triggered at different times than when viewing a page normally. Please keep that in mind when you test. I’ve learnt to double-check the page outside the tag assistant using the…

A browser’s network tab lets you look in detail at what gets sent to all your tracking systems. The payload tab for an individual request often gives you all the data you need. However, it does take some research to learn what the data means.

ParameterNotes
gcsThe original consent parameter. The last two numbers represent if ads and analytics consent is granted (1) or not (0)
gcdThe Consent Mode V2 parameter. It’s complex. Simo’s Consent Mode V2 article goes into details. The Consent Mode Inspector by InfoTrust Chrome extension decodes them for you.
emEnhanced Conversions data
dmaSet to 1 if the user is in a region that is covered by the Digital Markets Act. That is, people in the EEA.
dma_cps– or sypham (Spain, no consent set) so far?
pscdlSo far it seems its ‘denied’ if no ad_storage, and ‘noapi’ otherwise.
uidUser ID (user_id)
gdidCMS Developer ID that developers can use to indicate they built the solution.
GA4 URL parameters

Chrome extensions

I use quite a few Chrome extensions that help me monitor tracking systems:

ExtensionNotes
Consent Mode Inspector by InfoTrustDecodes the Google consent parameters for you.
I like the GCD Code tab which relates to Consent Mode V2.
Meta Pixel HelperFacebook/Meta Ads
Pinterest Tag HelperPinterest Ads
UET Tag Helper (by Microsoft Advertising)Microsoft/Bing Ads
Twitter Pixel HelperX Ads
Oath: Ad Platforms Dot HelperYahoo Ads
TikTok Pixel HelperTikTok Ads
Taboola Pixel HelperTaboola
Snap Pixel HelperSnapchat Ads
Reddit Pixel HelperReddit Ads
[DEPRECATED] Tag Assistant LegacyStill of some use to get a quick view of what Google destinations have been enabled.
Chrome Extensions for tracking tracking

Leave a Reply

Your email address will not be published. Required fields are marked *