GDPR
The General Data Protection Regulation (GDPR) of the European Union has been with us for many years. Data collection with Google Tag Manager is part of the compliance process for most of you. This also means that many GTM4WP users want to know how to stay GDPR-compliant while using this plugin.
This page describes GTM4WP 2.0.0 and newer. Where the legacy 1.x versions of the plugin behaved differently, the older behavior is marked with a bold GTM4WP 1.x: lead-in.
Disclaimer: I am not a lawyer, but I have participated in many GDPR-related projects at small and large companies. Here I explain the whole GTM and GDPR topic based on what I have learnt from these projects, but you should always discuss it with your own lawyers. What I can tell you below is logical and makes sense if you read the article carefully.
How GTM4WP fits into the GDPR compliance process
GTM4WP is a plugin that will help you integrate your Google Tag Manager container (GTM) into your website. For the purpose of simplicity, the term “personal data” should always be understood as “data that can identify a specific person directly or indirectly”.
What does “indirectly” mean? It refers to an identifier that does not identify someone on its own, but can be joined with another data set to make the identification possible. Take an order ID. If you have access to the list of orders in a webshop, the order ID tells you who placed that order. Without that access, the order ID alone does not lead you to a name or an email address.
There are two main cases where personal data processing can take place.
1. While collecting data with your tags
This one is obvious. If you add a marketing tag into your GTM container that collects personal data, you need to ask for the consent of your site visitor in order to do so. Here you need to see the process clearly to understand what can be done with GTM4WP and what is not the responsibility of GTM4WP.
Begin with the soup
As you know, by default, this plugin adds the so-called container code of GTM into your website. This ensures that the data collection mechanism in your GTM container can be executed while a user visits your site. However, this GTM container is empty by default. So if you activate this plugin and you ask the plugin to load an empty GTM container, almost nothing happens: no tag fires, so no tag collects or processes personal data.
I write almost, because one thing does happen. To load the container, the browser downloads gtm.js from www.googletagmanager.com, and every HTTP request carries the IP address and the user agent of the visitor. Google receives these even when the container is empty. Whether that single request on its own requires consent is a question for your lawyers, and the answer is not the same in every member state. What I can tell you is that the request does not have to go to Google at all. See server side tagging and the container request below.
Do you need to ask for consent before an empty container loads? In my opinion, no.
Do you need to block the loading of the GTM container itself? No!
Add some spicy ingredients
Now add a web analytics tracking tool into your GTM container.
(Side note: there is another misconception among users as to where personal data is collected. For example, if you are using Matomo to collect visitor data into your own server, that does not make the data collection GDPR-compliant, as the question is WHAT data is collected and processed and not WHERE you are storing the data)
Adding a web analytics tracking code means that there is a chance you will start collecting personal data. It is not necessarily the case, but it can happen. For example, if this web analytics tool processes the IP address of your visitor to populate geographic reports, you are likely in a situation where user consent needs to be handled. But there can be more sophisticated techniques like fingerprinting where someone combines individual, innocent data points and identifies an individual using lots of these innocent data points.
Question: In this case, what needs restricted usage if no consent has been given? The whole container or the tracking code that does the data collection? My answer is: the tracking code. Why would you want to block the whole container if you can block the tag with exception triggers?
Now add a second tracking code that will give you the opportunity to retarget previous site visitors. Does this require user consent? As far as I know, yes. Do you want to block the whole container if no consent has been given? My answer is: no! You should block the corresponding trigger.
Finally, add a tag that does not collect any GDPR-related data. For example, a custom HTML tag that replaces some texts on your site while developers are working on the same but that will release it only 2 weeks from now. No consent management is needed. If, in the first two cases, you say that you want to block the whole container, you will block this harmless tag as well.
Should GTM4WP have an option to block the loading of the container based on user consent?
I do not think so. If you have a consent solution, most probably it will store the chosen consent level into a cookie or a browser local storage object. This can be read by GTM itself. You can set up your firing and blocking triggers to stop data collection if proper user consent is missing. In some cases, this means that GTM4WP will load your GTM container, but GTM itself (!) will not fire any tag due to the lack of proper consent.
Google consent mode is the other half of this, and it is no longer optional if you advertise with Google in Europe. Since 6 March 2024 Google requires the ad_user_data and ad_personalization signals to arrive through consent mode for visitors from the European Economic Area and the United Kingdom. Without them, Google Ads and GA4 will not use that traffic for audiences or personalized advertising.
What GTM4WP gives you for consent handling
The settings page has a section called “Consent mode & consent tools”. It does not decide anything about consent on your behalf, it wires your consent tool to your container:
- The consent mode “default” command, fired before the container loads, with all seven flags: analytics_storage, ad_storage, ad_user_data, ad_personalization, functionality_storage, personalization_storage and security_storage. Every flag is denied unless you tick it. The “update” command has to come from your consent tool. Do not turn this on if your consent tool fires both commands itself, otherwise the default is sent twice.
- Cookiebot automatic cookie blocking.
- A CookieYes bridge that pushes a cookie_consent_update event into the data layer whenever CookieYes reports a consent choice, so your container has a defined signal to sequence tags on.
- Axeptio. The plugin can load the Axeptio SDK itself, so no separate Axeptio plugin is needed, and Axeptio can drive consent mode v2 with both the default and the update command. This is the one case where GTM4WP itself causes a 3rd party script to load, which is expected from a consent management tool but worth knowing before you enable it.
- WebToffee GDPR Cookie Consent v2.x. This one is deprecated. Version 3.x and above of that plugin talks to Google Tag Manager on its own, so upgrade the WebToffee plugin instead of using this option.
GTM4WP 1.x: the consent mode flags, Cookiebot and WebToffee are on the Integration tab. The CookieYes bridge and the Axeptio integration arrived with 2.0.0.
2. While using specific plugin features
Besides the GTM container, there is another entity that can be really useful while setting up your tracking in GTM. The data layer is a small data storage in the memory of your browser that is created by GTM4WP. It is important to know that the data layer gets created on each page load and gets destroyed while the page is unloaded from the browser. It does not provide persistent data storage.
Some plugin features add personal data into the data layer. For example, the name and email address of the customer in your WooCommerce shop. Every feature that puts personal data of your visitors or customers into the data layer is turned off by default.
How to deal with personal data in the data layer?
The first option is quite obvious: you can choose not to enable those features. Every option on the settings page describes what it adds to the data layer, so you can see what you are turning on before you turn it on. The Visitor IP option carries an explicit warning as well, since a full IP address must not be transmitted to Google Analytics or to any other measurement system without consent.
The second option is more complicated, but should work as well. In my opinion, as long as you do not set up tracking in your GTM container to read this personal data and transmit it to a specific service, I do not see any reason to ask for the user’s consent. If you set up such a tag, you will need to get the consent from the user using a consent banner software. Then read the status of the consent from the appropriate cookie and block this tag or the specific tag parameter if no consent has been given.
Most importantly, you are in charge, you are the one adding personal data processing into your measurement and you have the necessary controls in GTM to make it GDPR-compliant.
The cache-safe data layer
One sentence above needs a footnote. GTM4WP 2.0.0 has an option called “Cache-safe data layer”, made for sites where a page cache serves the same HTML to every visitor. With it turned on, visitor and user data is not printed into the page. The browser asks for it from a REST endpoint instead, and the plugin caches the answer so that the same visitor does not trigger a request on every page view. Part of that cache lives in sessionStorage, which survives page loads and is dropped when the browser tab is closed.
So with this option on, some of that data does get stored in the browser for the length of the session, which puts it into the same decision tree as any other storage: is it necessary for something the visitor asked for, or is it consent gated? Two short-lived cookies belong to this feature as well, both listed below. The option is experimental and off by default, so nothing of this applies unless you switch it on.
GTM4WP 1.x: the cache-safe data layer does not exist. 1.x always renders the data layer into the page.
And what about cookies stored by GTM4WP?
Specific GTM4WP features store cookies indeed. Before the list, one thing has to be said about the law, because cookies come up in almost every GDPR conversation while GDPR itself barely mentions them. GDPR is about the processing of personal data. The rule that governs storage in the browser is Article 5(3) of the ePrivacy Directive, transposed into the national law of each member state: you need consent before you store or read anything on the device of a visitor, unless that storage is strictly necessary for a service the visitor has asked for.
This is why I prefer the term consent banner over cookie banner. A visitor does not consent to a cookie. A visitor consents to data processing, and that processing may involve a cookie, a sessionStorage entry, a localStorage entry, or none of them. All of these go through the same question: necessary, or consent gated?
Here is everything GTM4WP 2.0.0 can store in the browser, so that you can run each item through that question yourself. Most of them only exist if you have turned on the feature that needs them.
| Name | Where and how long | What it holds | Can it be used to identify a person? |
|---|---|---|---|
| gtm4wp_user_logged_in | Cookie, until the browser is closed | The value 1, set when a user logs in, read and deleted on the next page load so that a GTM event can be fired | No. It carries no data about the person, only the fact that a login happened in this browser |
| gtm4wp_user_registered | Cookie, until the browser is closed | The value 1, set when a new user registers, read and deleted the same way | No, same as above |
| gtm4wp_orderid_tracked | localStorage, with a cookie as a fallback | The number of the last order that has been tracked, so that a page reload does not count the same purchase twice | Indirectly, in the sense described at the top of this page. It stays on the device of the buyer and the plugin does not send it anywhere |
| gtm4wp_item_list_attr | Cookie, first party | Up to 20 entries of a product ID with the name and ID of the product list it was clicked in, so that later events can report where the product came from | No. It holds product IDs and list names, nothing about the person |
| gtm4wp_woo_event | Cookie, 2 days, only with the cache-safe data layer on | The value 1. It tells the browser that a WooCommerce event is waiting in the session. Cleared once the event has fired | No. It carries no value about the visitor, only the fact that a fetch is due |
| gtm4wp_login | Cookie, 14 days, only with the cache-safe data layer on | A hash derived from the user ID and the login session token, so that the browser can notice that the logged in user has changed. It is not the session token and cannot be used to log in | Indirectly. It is tied to one user account, and it is scoped like the WordPress authentication cookies |
None of these carries a name, an email address, or an identifier that follows the visitor to another site. Two of them are indirect identifiers, but both stay on the device of the person they belong to, and the plugin transmits neither of them to Google or to any other service. They exist so that a feature you have switched on can work correctly. In my reading that puts them on the necessary side of the question above. Your data protection officer may read it differently, and that is a conversation worth having once you know what is actually stored, which is what the table is for.
GTM4WP 1.x: the first three entries work the same way in 1.x. The list attribution cookie and the two cache-safe cookies arrived with 2.0.0. 1.x also set a gtm4wp_last_weatherstatus cookie for the weather feature, which has been removed from 2.0.0 together with the feature itself.
Server side tagging and the container request
Server side tagging did not exist when I first wrote this article, and it changes part of the answer I gave above about the container loading request.
With server side tagging you run a second GTM container on a server that you control, reachable on your own subdomain. The browser then downloads the container script from your domain instead of www.googletagmanager.com and sends its events to your domain as well. The request that used to reach Google now reaches your server.
This does not make the IP address disappear. It only moves it to a server that you control. Your tagging server receives the IP address of the visitor, which means you are the one holding it and you are the one who decides what happens next. Every tag in the server container can be configured to forward the visitor IP or not. The GA4 tag, to take the obvious example, has a setting to redact the visitor IP address before the request leaves your server. So if you decide that no measurement platform should ever see the IP address of your visitors, you can implement that decision in one place for every platform at once. This article is not a guide on how to do it, but the control exists and it belongs to you.
Two things are worth knowing before you treat this as a complete answer. If you host the server container on Google Cloud, the requests still arrive at Google infrastructure, in the role of a hosting provider rather than the recipient of your measurement data. And the server container still forwards data to whatever platform you configure, so server side tagging on its own does not reduce what those platforms receive. What it gives you is a single place where you can make that decision.
GTM4WP supports this directly. Each container row on the settings page has a “Custom domain” and a “Custom path” field. Fill them in with the domain and the path of your tagging server and the plugin will load the container from there instead of www.googletagmanager.com. If your server container is selected by its path and expects no ID parameter in the URL, turn on “Omit container ID” in the same row.
GTM4WP 1.x: the custom domain and custom path are single settings for the whole plugin instead of one pair per container, and there is no option to omit the container ID.
Restricting what your container may run
There is one more control in the plugin that belongs to this discussion. The “Tag restrictions” section lets you decide which types of tags, triggers and variables are allowed to run on your site, whatever your container in the Google Tag Manager interface contains. GTM reads this restriction when it starts, so a tag type you have not allowed will not fire even if somebody adds it to the container later.
If your worry is that a colleague or an agency could publish a tag that starts collecting data you never agreed to, this addresses that worry far more precisely than blocking the container. Please read the warning on that settings screen before you change anything: a restriction that blocks a variable type will also break every tag that depends on it, and the result looks like the tag is broken rather than blocked.
GTM4WP 1.x: the same feature is on the Blacklist tab. The list of entities was refreshed in 2.0.0 based on the current documentation of Google.
That is all fine, but I still want to block the GTM container
If the above explanation does not satisfy your needs, there is still a way to block the loading of the container in case of missing user consent. Since there are many consent solutions on the market, there is no way I can provide you a unified way to achieve this. You will need a developer to make this work.
- Select the “Off” container placement option in the “Google Tag Manager container” section of the settings. This will disable the output of the container code, but will keep the data layer intact
- Create a code that checks for user consent. This might be JavaScript, or PHP if your consent solution makes its state available on the server. Put it into a child theme or into a code snippet plugin and hook it to wp_head or wp_body_open. Do not edit the header.php file of your theme, since the next theme update will overwrite it
- If user consent is present, output the container code by calling the gtm4wp_the_gtm_tag() function of the plugin
GTM4WP 1.x: the container placement option is on the General tab. The gtm4wp_the_gtm_tag() function works the same way in both versions.
This way, you will have full control over GTM container loading. Based on my standpoint above, I would really encourage you not to do so.

