Introductie: Waarom de keuze van opslag belangrijker is dan het lijkt
De meeste teams hebben vandaag een consent banner en een CMP op hun website staan. De juridische vakjes zijn afgevinkt, de banner verschijnt, en gebruikers kunnen cookies accepteren of weigeren. Klaar… toch? Niet helemaal.
Achter de schermen heeft de manier waarop je de toestemmingsstatus bewaart – en hoe je die doorgeeft aan Google Tag Manager en Consent Mode – een directe impact op:
-
de datakwaliteit in GA4 en andere tools
-
hoe goed conversiemodellering werkt
-
hoe eenvoudig server‑side tagging en back‑end integraties zijn
-
hoe vaak je gebruikers opnieuw om toestemming moet vragen
In de praktijk zijn er twee belangrijkste manieren waarop CMP’s de keuze van een gebruiker bewaren:
-
First‑party cookie
-
localStorage
Vanuit het standpunt van de CMP werken beiden. Vanuit een measurement-perspectief gedragen ze zich anders, en die verschillen zijn belangrijk.
Dit artikel legt uit:
-
hoe cookies en localStorage zich gedragen in een consent‑setup
-
hoe je beide koppelt aan GTM en Google Consent Mode met zo weinig mogelijk custom code
-
wanneer je voor welke aanpak kiest, met concrete scenario’s
1. First‑Party Cookies vs localStorage: hetzelfde doel, ander gedrag
Beide mechanismen worden gebruikt om te “onthouden” dat een gebruiker toestemming heeft gegeven, geweigerd of gepersonaliseerd. Maar onder de motorkap werken ze heel verschillend.
1.1 First-party cookie – belangrijkste eigenschappen
-
Wordt door de browser opgeslagen en met elke HTTP‑request naar je domein meegestuurd.
-
Heeft een vervaldatum (bijv. 6 maanden).
-
Is heel vroeg beschikbaar in de levenscyclus van de pagina (vanaf de allereerste request).
-
Kan gelezen worden door:
-
het script van je CMP op de pagina,
-
GTM (via een first‑party cookie‑variabele),
-
server‑side GTM / back‑ends (uit de HTTP‑headers).
-
Implicaties voor consent:
-
De consent state is beschikbaar zodra er iets draait, wat ideaal is voor Consent Mode defaults en server‑side logica.
-
Wanneer gebruikers cookies verwijderen, verwijderen ze ook hun consent → de banner verschijnt opnieuw bij een volgend bezoek.
1.2 localStorage – belangrijkste eigenschappen
-
Wordt enkel in de browser opgeslagen; wordt niet meegestuurd met HTTP‑requests.
-
Heeft geen ingebouwde vervaldatum; blijft staan tot het expliciet wordt verwijderd (of site‑data gewist wordt).
-
Is pas beschikbaar wanneer JavaScript op de pagina wordt uitgevoerd.
-
Is niet zichtbaar voor server‑side GTM of je backend, tenzij je het zelf doorstuurt.
Implicaties voor consent:
-
Er is altijd een kleine timinggap: de pagina moet JS draaien om localStorage te lezen en consent bloot te leggen.
-
De consent status overleeft het verwijderen van cookies, tenzij de gebruiker alle site‑data verwijdert.
-
Server‑side / back‑end systemen zien dit niet rechtstreeks; ze moeten consent meekrijgen via tags (bijv. GA4 → sGTM) of via custom parameters.
1.3 Gedrag wanneer gebruikers hun browser ‘opschonen’
Dit is een van de belangrijkste praktische verschillen:
Als consent in een cookie staat:
-
Cookies verwijderen = consent verwijderen.
-
Gebruikers zullen de banner vaker opnieuw zien.
Als consent in localStorage staat:
-
Cookies verwijderen wist de consent‑waarde niet.
-
Gebruikers zien de banner minder vaak → minder “consent‑moeheid” en stabielere consent‑ratio’s.
Er is geen universeel “beste” optie; het is een trade‑off tussen UX‑stabiliteit en een strikte koppeling tussen cookie‑aanwezigheid en consent.
2. Consent implementeren met first-party cookies (GTM-voorbeeld)
Er zijn veel geldige manieren om een CMP, GTM en Consent Mode met elkaar te verbinden.
Onderstaande setup is één concreet voorbeeld, waarbij we:
-
een CMP gebruiken die consent opslaat in een first‑party cookie,
-
de 1st‑party cookie‑variabele van GTM gebruiken om die status uit te lezen,
-
een eenvoudige mapping maken naar Google Consent Mode.
2.1 Aannames
Voor dit voorbeeld gaan we uit van het volgende:
-
Je CMP schrijft een cookie met de naam cmp_consent zodra de gebruiker een keuze maakt.
-
De cookie‑waarde ziet er als volgt uit:
cmp_consent=ad_storage=granted|analytics_storage=denied|ad_user_data=denied|ad_personalization=denied
-
ad_storage komt overeen met:
“If set to denied, Google and Microsoft's advertising tags and pixels will not be able to read or write first‑party cookies.”
-
analytics_storage komt overeen met:
“If set to denied, Google Analytics tags will not read or write analytics cookies, and data collected to Google Analytics will not utilize persistent cookie identifiers (the identifiers are reset with every page load).”
-
ad_user_data komt overeen met:
“If set to denied, user data cannot be used with Google's advertising solutions for audience building.”
-
ad_personalization komt overeen met:
“If set to denied, data collected on this website will not be used for remarketing in Google's advertising solutions.”
Je kan benamingen en format uiteraard aanpassen aan je eigen CMP.
Voor dit voorbeeld gebruiken we de Simo Ahava Consent Mode‑template om consent te configureren.
2.2 Stap 1 – Laat de CMP de consent‑cookie schrijven
Dit gebeurt meestal in de CMP‑interface of scriptconfiguratie, niet in GTM.
Typische configuratie in je CMP:
-
Storage‑methode: Cookie / First‑party cookie
-
Cookienaam: cmp_consent
Wanneer de gebruiker zijn voorkeuren opslaat:
-
Als hij alles accepteert →
ad_storage=granted|analytics_storage=granted|ad_user_data=granted|ad_personalization=granted
-
Als hij alles weigert →
ad_storage=denied|analytics_storage=denied|ad_user_data=denied|ad_personalization=denied
-
Bij aangepaste instellingen → een mix van granted/denied.
Je hoeft dit meestal niet zelf te hardcoden; zorg wel dat je volgende informatie kent:
-
de cookienaam
-
het formaat van de waarde
2.3 Stap 2 – Maak de cookie zichtbaar voor GTM via een 1st‑party cookie‑variabele
Nu zorgen we ervoor dat GTM die cookie kan lezen.
-
Ga naar Variables → New
-
Variabeletype: 1st‑Party Cookie
-
Naam: Cookie – cmp_consent
-
Cookie name: cmp_consent
Deze variabele zal nu een string teruggeven zoals:
ad_storage=granted|analytics_storage=granted|ad_user_data=granted|ad_personalization=granted
zodra de cookie bestaat.

2.4 Stap 3 – Haal elke consent‑waarde uit de cookie met Regex‑variabelen
Op dit moment geeft {{Cookie – cmp_consent}} de volledige string terug, bijvoorbeeld:
ad_storage=granted|analytics_storage=denied|ad_user_data=denied|ad_personalization=denied
Simo’s Consent Mode‑template verwacht vier variabelen, één per consent‑signaal, die elk exact granted of denied teruggeven.
Een eenvoudige manier om dat te bereiken is met Regex Table‑variabelen.
2.4.1 Maak Regex – ad_storage
-
Ga naar Variables → New
-
Type: Regex Table
-
Naam: Regex – ad_storage
-
Input variable: {{Cookie – cmp_consent}}
Vul de tabel als volgt in:
-
Pattern: (^|\|)ad_storage=granted(\||$)
-
Output: granted
-
Default value: denied
Als de cookie ad_storage=granted bevat, geeft deze variabele granted terug.
Ontbreekt het veld of is het ongeldig, dan wordt standaard denied gebruikt.

2.4.2 Maak Regex – analytics_storage
Zelfde idee:
-
Variables → New → Regex Table
-
Naam: Regex – analytics_storage
-
Input variable: {{Cookie – cmp_consent}}
Tabel:
-
Pattern: (^|\|)analytics_storage=granted(\||$)
-
Output: granted
-
Default value: denied
2.4.3 Maak Regex – ad_user_data en Regex – ad_personalization
Herhaal voor de twee V2‑specifieke signalen.
ad_user_data
-
Naam: Regex – ad_user_data
-
Pattern: (^|\|)ad_user_data=granted(\||$)
-
Output: granted
-
Default: denied
ad_personalization
-
Naam: Regex – ad_personalization
-
Pattern: (^|\|)ad_personalization=granted(\||$)
-
Output: granted
-
Default: denied
Op dit punt heb je vier variabelen:
-
{{Regex – ad_storage}}
-
{{Regex – analytics_storage}}
-
{{Regex – ad_user_data}}
-
{{Regex – ad_personalization}}
Elke variabele geeft granted of denied terug, precies zoals Simo’s template verwacht.
2.5 Stap 4 – Configureer de Simo Ahava Consent Mode‑tag
Nu koppelen we die variabelen aan de Consent Mode (Google tags)‑tagtemplate.
2.5.1 Voeg de template toe aan je container
-
Ga in GTM naar Templates → Search Gallery
-
Zoek op “Consent Mode (Google tags)”
-
Voeg de template van Simo Ahava toe
Je hebt nu een nieuw tagtype beschikbaar.
2.5.2 Maak de “default” Consent Mode‑tag
-
Ga naar Tags → New
-
Tag type: Consent Mode (Google tags)
-
Naam: Consent Mode – default (from cookie)
Configureer:
-
Command type: default
-
Region: all (of een lijst zoals BE,FR,NL)
-
ad_storage: {{Regex – ad_storage}}
-
analytics_storage: {{Regex – analytics_storage}}
-
ad_user_data: {{Regex – ad_user_data}}
-
ad_personalization: {{Regex – ad_personalization}}
Laat wait_for_update op een waarde die past bij je CMP‑gedrag (bijv. 500–1000 ms).
4. Trigger: Consent Initialization – All Pages
Wanneer deze tag afvuurt:
-
Leest hij de cookie via je Regex‑variabelen
-
Stuurt hij een default‑commando met de vier consent‑statussen
-
Weet GA4 / Google Ads / andere Google‑tags meteen welke consent‑status ze moeten respecteren
2.5.3 Optioneel: “update” Consent Mode‑tag
De meeste CMP’s pushen ook een dataLayer‑event zodra de gebruiker zijn voorkeuren opslaat, bijvoorbeeld:
dataLayer.push({
event: 'cmp_consent_updated'
});
Als je CMP niet zelf Consent Mode aanroept, kun je:
-
Een tweede tag maken met dezelfde template:
Consent Mode – update (from cookie)
-
Command type: update
-
Opnieuw dezelfde vier Regex‑variabelen gebruiken (die nu de nieuwe cookie‑waarde lezen na de keuze van de gebruiker)
-
Trigger: Custom Event → event name: cmp_consent_updated
Zo blijft je volledige Consent Mode‑configuratie binnen GTM en aangestuurd door de CMP‑cookie.
2.6 Praktisch voorbeeld: van CMP‑keuze tot werkende Consent Mode
Om het concreet te maken, stel je de volgende flow voor:
-
Gebruiker komt voor het eerst op je site.
-
Er is nog geen cmp_consent‑cookie.
-
Je Regex‑variabelen vallen allemaal terug op denied.
-
De default‑tag van Simo vuurt af bij Consent Initialization met:
-
ad_storage: denied
-
analytics_storage: denied
-
ad_user_data: denied
-
ad_personalization: denied
-
-
-
GA4 en Google Ads zitten daardoor in een privacy‑veilige “no consent” modus.
-
Gebruiker accepteert enkel analytics, en weigert advertising cookies.
-
CMP schrijft: cmp_consent=ad_storage=denied|analytics_storage=granted|ad_user_data=denied|ad_personalisation=denied
-
CMP pusht cmp_consent_updated.
-
Je Consent Mode update‑tag stuurt:
-
analytics_storage: granted
-
ad_storage: denied
-
ad_user_data: denied
-
ad_personalization: denied
-
-
-
GA4 mag nu meten, Ads blijft beperkt.
-
Bij de volgende pageview is de cookie nog aanwezig.
-
De default‑tag leest de cookie opnieuw via de Regex‑variabelen en zet dezelfde statussen meteen terug.
-
Geen custom parse‑code nodig, en de logica blijft netjes leesbaar in de GTM UI.
-
Dit is maar één mogelijke pattern, maar het illustreert mooi hoe first‑party cookies + Regex‑variabelen + Simo’s template samenkomen.
3. Consent implementeren met localStorage (GTM‑voorbeeld)
Dezelfde filosofie geldt wanneer je CMP consent bewaart in localStorage in plaats van in een cookie.
Ook dit is slechts één manier; het idee is om te tonen hoe je:
-
localStorage leest via een eenvoudige JavaScript‑variabele,
-
waarden normaliseert via Regex / Lookup‑variabelen,
-
ze in dezelfde Consent Mode‑template voert.
3.1 Aannames
Voor dit voorbeeld gaan we uit van:
-
Je CMP bewaart consent in localStorage onder de key cmpConsent.
-
De waarde is één string, in hetzelfde formaat als de cookie:
ad_storage=granted|analytics_storage=denied|ad_user_data=denied|ad_personalisation=denied
(Gebruikt je CMP JSON, dan is de logica gelijkaardig – je past alleen de extractie aan.)
3.2 Stap 1 – Zorg dat de CMP naar localStorage schrijft
In de CMP‑UI / config:
-
Storage‑methode: localStorage
-
Key: cmpConsent
Bij een keuze van de gebruiker:
-
Schrijft de CMP bovenstaande string (of equivalent) naar localStorage.
-
Optioneel pusht hij opnieuw een cmp_consent_updated‑event in de dataLayer (hetzelfde event als in het cookie‑voorbeeld).
3.3 Stap 2 – Lees localStorage in GTM met een JavaScript‑variabele
-
Ga naar Variables → New
-
Type: Custom JavaScript
-
Naam: JS – cmpConsent (localStorage)
-
Code:
function () {
try {
var raw = window.localStorage.getItem('cmpConsent');
return raw || '';
} catch (e) {
return '';
}
}
Deze variabele retourneert de volledige string, bijvoorbeeld:
ad_storage=granted|analytics_storage=denied|ad_user_data=denied|ad_personalisation=denied
zodra de gebruiker eerder een keuze heeft gemaakt.
3.4 Stap 3 – Haal elke consent‑waarde uit localStorage met Regex‑variabelen
Je kan nu exact dezelfde Regex‑aanpak herhalen, maar met de localStorage‑variabele als input.
3.4.1 Regex – ad_storage (LS)
-
Variables → New → Regex Table
-
Naam: Regex – ad_storage (LS)
-
Input variable: {{JS – cmpConsent (localStorage)}}
Tabel:
-
Pattern: (^|\|)ad_storage=granted(\||$)
-
Output: granted
-
Default value: denied
3.4.2 Regex – analytics_storage (LS)
Zelfde patroon:
-
Naam: Regex – analytics_storage (LS)
-
Input: {{JS – cmpConsent (localStorage)}}
-
Pattern: (^|\|)analytics_storage=granted(\||$)
-
Output: granted
-
Default: denied
3.4.3 Regex – ad_user_data (LS) en Regex – ad_personalization (LS)
-
Regex – ad_user_data (LS)
-
Pattern: (^|\|)ad_user_data=granted(\||$) → Output granted
-
Default: denied
-
-
Regex – ad_personalization (LS)
-
Pattern: (^|\|)ad_personalization=granted(\||$) → Output granted
-
Default: denied
-
Elke variabele geeft nu opnieuw exact granted of denied terug.
3.5 Stap 4 – Configureer de Consent Mode‑tag voor localStorage
Je kan:
-
dezelfde Simo‑template gebruiken in een nieuwe tag, of
-
de bestaande tag hergebruiken en enkel de input‑variabelen omwisselen (als je maar één aanpak tegelijk test).
Typische setup:
-
Tags → New
-
Type: Consent Mode (Google tags)
-
Naam: Consent Mode – default (from localStorage)
Velden:
-
Command type: default
-
Region: all (of een specifieke lijst)
-
ad_storage: {{Regex – ad_storage (LS)}}
-
analytics_storage: {{Regex – analytics_storage (LS)}}
-
ad_user_data: {{Regex – ad_user_data (LS)}}
-
ad_personalization: {{Regex – ad_personalization (LS)}}
Trigger: Consent Initialization – All Pages
Als de CMP ook cmp_consent_updated triggert wanneer de gebruiker zijn keuze wijzigt en nieuwe localStorage‑waarden schrijft, kan je:
-
een update‑tag maken met dezelfde vier LS‑Regex‑variabelen,
-
getriggerd op hetzelfde cmp_consent_updated‑event.
3.6 Praktisch voorbeeld: localStorage houdt consent stabiel
Een voorbeeldflow:
-
Dag 1 – eerste bezoek:
-
Geen cmpConsent in localStorage.
-
Alle LS Regex‑variabelen vallen terug op denied.
-
De default‑tag van Simo zet alle Consent Mode‑flags op denied.
-
GA4 en Ads gedragen zich alsof er geen toestemming is.
-
-
Gebruiker accepteert alles in de CMP:
-
CMP schrijft:localStorage.cmpConsent = "ad_storage=granted|analytics_storage=granted|ad_user_data=granted|ad_personalisation=granted"
-
CMP pusht cmp_consent_updated.
-
Jouw update‑tag stuurt een Consent Mode update met alle vier de signalen op granted.
-
-
Dag 10 – gebruiker wist cookies, maar geen site‑data:
-
De CMP‑cookie (als die bestond) is weg, maar localStorage.cmpConsent bestaat nog.
-
Bij de volgende pageview:
-
{{JS – cmpConsent (localStorage)}} geeft de opgeslagen string terug.
-
De Regex‑variabelen geven granted terug voor alle vier consenttypes.
-
De Consent Mode default‑tag zet alles meteen op granted.
-
-
De gebruiker ziet de banner niet opnieuw (als je CMP de consent nog geldig vindt), en je metingen blijven consistent, ondanks het wissen van cookies.
Conclusie
Of je CMP consent nu opslaat in een first‑party cookie of in localStorage, het belangrijkste is hoe dat signaal wordt vertaald naar Google Tag Manager en Consent Mode. First‑party cookies geven je een vroeg, server‑zichtbaar consent‑signaal dat eenvoudig herbruikbaar is in sGTM en back‑ends, terwijl localStorage de nadruk legt op UX‑stabiliteit omdat het de meeste cookie‑opschoonacties overleeft.
De implementaties in dit artikel – met GTM‑cookievariabelen, JavaScript/localStorage‑variabelen, Regex‑extractie en de Simo Ahava Consent Mode‑template – zijn slechts één manier om alles aan elkaar te koppelen. Ze illustreren het kernprincipe: kies één bron van waarheid voor consent, normaliseer die in GTM en laat Consent Mode die keuze consistent afdwingen over al je tags. Eens dat fundament er ligt, kan je de details afstemmen op je CMP, je juridische vereisten en je measurement‑stack.
