Google Consent Mode in een Shopify custom pixel (gtag.js)
Overzicht
Sommige winkels laden Google Analytics 4 (G-) of Google Ads (AW-) rechtstreeks met gtag.js vanuit een Shopify custom pixel (Settings > Customer events). Dit artikel geeft je kant-en-klare pixelcode die Google Consent Mode Advanced toepast op die eigenschappen, met de toestemming die je bezoekers geven in de Pandectes-banner.
Worden je Google-eigenschappen geladen via een Google Tag Manager-container, gebruik dan in plaats daarvan Google Consent Mode in een Shopify custom pixel (Google Tag Manager).
Dit is een implementatiegids. Voor achtergrond over default- en update-commando's en over de Geavanceerde modus, zie Google's Consent Mode met gtag.js.
Voordat je begint
Schakel eerst de Google Consent Mode-integratie in
Deze pixel is één helft van een opzet in twee delen. Ga in Shopify Admin naar Apps > Pandectes GDPR > Integrations, klik op Configure bij de kaart Google Consent Mode, en zet de integratie aan. Zie Google Consent Mode-integratie gebruiken. Google Consent Mode werkt vanaf het Plus-abonnement en hoger.
Een Shopify custom pixel draait in een afgeschermd iframe met zijn eigen window, dataLayer en een kopie van gtag.js. Die staat los van de storefront-pagina, en toestemming die in de ene is ingesteld, is in de andere niet zichtbaar. Elk heeft dus zijn eigen instelling nodig:
Waar | Wat daar draait | Wat de consent mode instelt |
|---|---|---|
Storefront-pagina | Google-tags in | De Pandectes Google Consent Mode-integratie |
Pixel-sandbox | De | De code in dit artikel |
Gebruik je Google Tag Manager?
Als je winkel een GTM-container heeft, controleer dan of je Google-eigenschappen daar al worden geladen. Dat is meestal het geval. Een pixel toevoegen die dezelfde GA4- of Google Ads-eigenschap opnieuw laadt, dubbelt je paginaweergaven, sessies, aankopen en conversies, en maakt twee concurrerende toestemmingsstatussen.
GA4 of Google Ads geladen door een GTM-container | Google Consent Mode in een Shopify custom pixel (Google Tag Manager) |
|---|---|
Geen GTM, eigenschappen geladen met | Dit artikel |
Wat deze code dekt
De code stelt alleen de toestemmingsstatus in en verder niets. Ze stuurt het standaardbeleid van de winkel naar Google, past de keuze van de bezoeker toe, houdt die gelijk met de banner, en laadt de Google-tag zodat de toestemmingssignalen een bestemming hebben. Ze stuurt zelf geen analyse- of conversiedata.
Je event-abonnementen staan in dezelfde pixel
Het versturen van Shopify's klantevents naar Google is het echte doel van de pixel, en die abonnementen moeten in dezelfde pixel staan, onder de toestemmingscode. De voorbeelden in elk fragment staan bewust uitgecommentarieerd. Haal de commentaartekens weg of vervang ze door je eigen code.
Elke pixel die een Google-eigenschap laadt, heeft een eigen kopie nodig
Je kunt de toestemmingscode niet in de ene custom pixel zetten en je events in een andere. Elke pixel is een aparte sandbox. Een pixel die GA4 of Google Ads laadt zonder de toestemmingscode, vuurt alles af volgens de standaardwaarden van Google, alsof er geen consent mode bestaat, en Shopify waarschuwt je daar niet voor.
- Eén pixel laadt je eigenschappen: één kopie van de code, met de events van die pixel eronder.
- Drie pixels laden elk een Google-eigenschap: drie kopieën, één in elke pixel.
- Als meerdere pixels dezelfde eigenschap laden, los die dubbeling dan eerst op.
Geavanceerde modus en de pixel-toestemming
Deze code geeft je Google Consent Mode Advanced: de Google-tag laadt bij elke paginaweergave, stuurt cookieloze pings zolang de toestemming is geweigerd, en schakelt over op volledige meting zodra de toestemming is verleend. Daardoor kan Google conversies modelleren voor bezoekers die niet instemmen.
Daarvoor moet de pixel vóór toestemming mogen draaien. Ga in Shopify Admin naar Settings > Customer events, open je custom pixel, en stel onder Customer privacy Permission in op Not required. Als je het op Required zet, houdt Shopify de pixel tegen tot de bezoeker accepteert, dus de Google-tag laadt nooit vóór toestemming en er worden geen cookieloze pings verstuurd.
Als je Basic mode wilt (niets laadt tot er toestemming is gegeven), heb je deze code niet nodig. Zet Permission op Required, selecteer de categorieën die je nodig hebt, en laat Shopify de pixel tegenhouden.
Hoe de code werkt
Shopify geeft de pixel de toestemming van de bezoeker in init.customerPrivacy, maar zegt niet of dat het standaardbeleid van de winkel is of een keuze die de bezoeker al heeft gemaakt. De code voert dus altijd deze stappen uit, in deze volgorde:
consentdefault: het beleid van de winkel, in de code geschreven.consentupdate: watinit.customerPrivacyook bevat. Als het alleen de standaard was, verandert er niets. Als het een opgeslagen keuze was, wordt die toegepast vóór de eerste hit.- Laad
gtag.jsen configureer je Google-eigenschappen. - Luister naar
visitorConsentCollecteden stuur elke keer eenupdateals de bezoeker zijn keuze wijzigt.
In een custom pixel zijn analytics, browser en init direct beschikbaar, maar customerPrivacy bereik je via api. Het aanroepen van customerPrivacy.subscribe(...) zonder api. geeft een ReferenceError.
De toestemmingswaarden van Shopify worden als volgt aan Google's toestemmingstypen gekoppeld:
Shopify | |
|---|---|
|
|
|
|
|
|
| Geen Consent Mode-equivalent. Het is een signaal voor verkoop of delen in een Amerikaanse staat (zie |
(altijd) |
|
Welke snippet heb ik nodig?
Zelfde beleid wereldwijd: niets draait tot de bezoeker toestemming geeft | Snippet 1 (één standaardcommando) |
|---|---|
Toestemming alleen in sommige regio's verzameld, elders toegestaan | Snippet 2 (standaardcommando's per regio) |
Beide snippets ondersteunen GA4, Google Ads, of beide.
- Ga in Shopify Admin naar Settings > Customer events en klik op Add custom pixel.
- Geef de pixel een naam en plak een van de snippets.
- Vul je ID's in tussen
START_MODIFY_FOR_YOUR_STOREenEND_MODIFY_FOR_YOUR_STORE. - Voeg onderaan je eigen event-abonnementen toe.
- Stel de toestemming in zoals hierboven beschreven, klik op Save, en daarna op Connect.
Snippet 1: één standaardcommando
Eén default-commando zonder regio, zodat het voor elke bezoeker geldt. Alles is geweigerd behalve security_storage.
/* START_MODIFY_FOR_YOUR_STORE */
const GA4_ID = ""; // e.g. "G-XXXXXXXXXX", leave empty if not used
const AW_ID = ""; // e.g. "AW-XXXXXXXXXX", leave empty if not used
const DEBUG = false; // true sends GA4 hits to DebugView while testing
/* END_MODIFY_FOR_YOUR_STORE */
const GRANTED = "granted";
const DENIED = "denied";
const GOOGLE_URL = "https://www.googletagmanager.com/gtag/js?id=";
const DEVELOPER_ID = "dMTZkMj";
/* Shopify customerPrivacy -> Google consent state */
const toConsentState = (cp) => ({
ad_storage: cp.marketingAllowed ? GRANTED : DENIED,
ad_user_data: cp.marketingAllowed ? GRANTED : DENIED,
ad_personalization: cp.marketingAllowed ? GRANTED : DENIED,
analytics_storage: cp.analyticsProcessingAllowed ? GRANTED : DENIED,
functionality_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
personalization_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
security_storage: GRANTED,
});
/* The pixel sandbox has its own dataLayer. */
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
/* Pandectes developer ID. Identifies the implementation to Google,
* has no effect on your data. Leave it as it is. */
gtag("set", `developer_id.${DEVELOPER_ID}`, true);
/* STEP 1: store policy, before gtag.js loads. Applies to everyone. */
gtag("consent", "default", {
ad_storage: DENIED,
ad_user_data: DENIED,
ad_personalization: DENIED,
analytics_storage: DENIED,
functionality_storage: DENIED,
personalization_storage: DENIED,
security_storage: GRANTED,
});
/* STEP 2: visitor state, always sent. If it is only the default,
* nothing changes. If it is a saved choice, it lands before the first hit. */
gtag("consent", "update", toConsentState(init.customerPrivacy));
/* STEP 3: load the Google tag once and configure each property. */
const TAG_IDS = [GA4_ID, AW_ID].filter(Boolean);
if (TAG_IDS.length) {
gtag("js", new Date());
if (GA4_ID) {
gtag("config", GA4_ID, {
send_page_view: false,
...(DEBUG ? { debug_mode: true } : {}),
});
}
if (AW_ID) {
gtag("config", AW_ID);
}
const script = document.createElement("script");
script.async = true;
script.src = GOOGLE_URL + TAG_IDS[0];
document.head.appendChild(script);
}
/* STEP 4: follow the visitor's choices. Note the api. prefix. */
api.customerPrivacy.subscribe("visitorConsentCollected", (event) => {
gtag("consent", "update", toConsentState(event.customerPrivacy));
});
/* YOUR EVENT SUBSCRIPTIONS GO HERE, IN THIS SAME PIXEL.
* The examples are commented out on purpose. What you send is yours
* to own and verify. */
/* Page view: only if the storefront does not already send page_view
* for the same property, or it is counted twice. */
// analytics.subscribe("page_viewed", (event) => {
// gtag("event", "page_view", {
// page_path: event.context.document.location.pathname,
// page_title: event.context.document.title,
// page_location: event.context.window.location.href,
// });
// });
/* Purchase: if GA4 and Google Ads are linked, import the GA4 purchase
* as a conversion in Google Ads and delete the conversion block below,
* or every order is counted twice. */
// analytics.subscribe("checkout_completed", (event) => {
// const checkout = event.data.checkout;
// const items = checkout.lineItems.map((line) => ({
// item_id: "shopify_" + line.variant.product.id + "_" + line.variant.id,
// item_name: line.title,
// item_variant: line.variant.title,
// price: line.variant.price.amount,
// quantity: line.quantity,
// }));
//
// gtag("event", "purchase", {
// transaction_id: checkout.token,
// currency: checkout.currencyCode,
// value: checkout.totalPrice.amount,
// tax: checkout.totalTax.amount,
// shipping: checkout.shippingLine ? checkout.shippingLine.price.amount : 0,
// items: items,
// });
//
// // Google Ads conversion, ONLY when GA4 and Google Ads are NOT linked.
// // Use the conversion label from your Google Ads conversion action.
// gtag("event", "conversion", {
// send_to: "AW-XXXXXXXXXX/AbC-D_efGhIjKlMnOp",
// transaction_id: checkout.token,
// currency: checkout.currencyCode,
// value: checkout.totalPrice.amount,
// });
// });
Snippet 2: standaardcommando's per regio
Voor winkels die alleen toestemming vragen waar dat wettelijk verplicht is. Er zijn twee default-commando's: een globaal commando dat alles toestaat, en een voor de vermelde regio's dat alles weigert behalve security_storage. Als beide op een bezoeker van toepassing zijn, wint de specifiekere regio.
De rest is gelijk aan Snippet 1.
/* START_MODIFY_FOR_YOUR_STORE */
const GA4_ID = ""; // e.g. "G-XXXXXXXXXX", leave empty if not used
const AW_ID = ""; // e.g. "AW-XXXXXXXXXX", leave empty if not used
/* ISO 3166-2 codes where the store collects consent: EEA + UK below.
* Add US states ("US-CA", "US-CO", ...) if you treat them as opt-in.
* This list MUST match the regions where your banner collects consent. */
const RESTRICTED_REGIONS = [
"AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
"DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
"PL", "PT", "RO", "SK", "SI", "ES", "SE", "GB", "LI", "NO", "IS",
];
const DEBUG = false; // true sends GA4 hits to DebugView while testing
/* END_MODIFY_FOR_YOUR_STORE */
const GRANTED = "granted";
const DENIED = "denied";
const GOOGLE_URL = "https://www.googletagmanager.com/gtag/js?id=";
const DEVELOPER_ID = "dMTZkMj";
/* Shopify customerPrivacy -> Google consent state */
const toConsentState = (cp) => ({
ad_storage: cp.marketingAllowed ? GRANTED : DENIED,
ad_user_data: cp.marketingAllowed ? GRANTED : DENIED,
ad_personalization: cp.marketingAllowed ? GRANTED : DENIED,
analytics_storage: cp.analyticsProcessingAllowed ? GRANTED : DENIED,
functionality_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
personalization_storage: cp.preferencesProcessingAllowed ? GRANTED : DENIED,
security_storage: GRANTED,
});
/* The pixel sandbox has its own dataLayer. */
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
/* Pandectes developer ID. Identifies the implementation to Google,
* has no effect on your data. Leave it as it is. */
gtag("set", `developer_id.${DEVELOPER_ID}`, true);
/* STEP 1a: policy for everyone outside the listed regions. */
gtag("consent", "default", {
ad_storage: GRANTED,
ad_user_data: GRANTED,
ad_personalization: GRANTED,
analytics_storage: GRANTED,
functionality_storage: GRANTED,
personalization_storage: GRANTED,
security_storage: GRANTED,
});
/* STEP 1b: policy for the listed regions. Google resolves the region
* from the IP address, so test it with a VPN. */
gtag("consent", "default", {
ad_storage: DENIED,
ad_user_data: DENIED,
ad_personalization: DENIED,
analytics_storage: DENIED,
functionality_storage: DENIED,
personalization_storage: DENIED,
security_storage: GRANTED,
region: RESTRICTED_REGIONS,
});
/* STEP 2: visitor state, always sent. Shopify has already applied your
* regional settings to init.customerPrivacy, so this matches 1a or 1b. */
gtag("consent", "update", toConsentState(init.customerPrivacy));
/* STEP 3: load the Google tag once and configure each property. */
const TAG_IDS = [GA4_ID, AW_ID].filter(Boolean);
if (TAG_IDS.length) {
gtag("js", new Date());
if (GA4_ID) {
gtag("config", GA4_ID, {
send_page_view: false,
...(DEBUG ? { debug_mode: true } : {}),
});
}
if (AW_ID) {
gtag("config", AW_ID);
}
const script = document.createElement("script");
script.async = true;
script.src = GOOGLE_URL + TAG_IDS[0];
document.head.appendChild(script);
}
/* STEP 4: follow the visitor's choices. Note the api. prefix. */
api.customerPrivacy.subscribe("visitorConsentCollected", (event) => {
gtag("consent", "update", toConsentState(event.customerPrivacy));
});
/* YOUR EVENT SUBSCRIPTIONS GO HERE, IN THIS SAME PIXEL.
* Use the same page view and purchase examples as in Snippet 1. */
Controleer of het werkt
Tag Assistant kan de pixel niet zien
Google Tag Assistant inspecteert alleen de storefront-pagina. De gtag, dataLayer en eigenschappen van de pixel staan in een afgeschermd iframe waar het niet bij kan, dus het meldt niets (of alleen de eigen tags van de storefront), ook als de pixel correct werkt. Dit is normaal en geen fout.
Controleer de dataLayer in het frame van de pixel
- Open Chrome DevTools en ga naar het tabblad Console.
- Open in de consolebalk het frame-menu (dat meestal
toptoont) en selecteer het sandbox-frame van de pixel (de naam begint metweb-pixel). Shopify maakt één frame per custom pixel, dus kies het juiste frame als je er meerdere hebt. - Voer onderstaande controles uit.
In het top-frame is dataLayer de dataLayer van de storefront, die niets over de pixel vertelt.
- Komen de standaardwaarden eerst? In
dataLayermoeten deconsent-regels vóór deconfig-regels komen. - Bereikt de toestemming de tag? Stel
DEBUG = truein, zet een event aan zodat er iets wordt verstuurd, en open GA4 DebugView. Metanalytics_storagegeweigerd zie je een cookieloze ping in plaats van een normale hit. - Laadt de pixel vóór toestemming? Dat moet, voor de Geavanceerde modus. Als de Google-tag pas verschijnt nadat de bezoeker accepteert, staat de pixel-toestemming op Required en werk je in de Basismodus.
- Werkt de regiologica? Test met een VPN binnen en buiten een vermeld land.
- Wordt het abonnement uitgevoerd? Voeg tijdelijk een
console.logtoe binnen devisitorConsentCollected-callback en wijzig je keuze in de banner.
Veelgemaakte fouten
Fout | Wat er gebeurt |
|---|---|
Toestemmingscode in de ene pixel, events in een andere | De events-pixel heeft geen |
Meerdere pixels laden een Google-eigenschap, maar slechts één heeft de toestemmingscode | De andere vuren af zonder consent mode, zonder waarschuwing. |
Pixel Permission ingesteld op Required | De tag laadt nooit vóór toestemming: geen cookieloze pings en geen modellering. Dat is de Basismodus. |
Ads- | Elke bestelling wordt dubbel geteld in Google Ads. |
|
|
De | Terugkerende bezoekers met opgeslagen toestemming krijgen geweigerde tags bij hun eerste paginaweergave. |
| Consent Mode wordt niet toegepast op de eerste hits. |
Google Consent Mode-integratie niet ingeschakeld in Pandectes | Alleen de eigenschappen van de pixel krijgen consent mode. Google-tags op de storefront draaien er zonder. |
Pixel gebouwd terwijl GTM dezelfde eigenschap al laadt | Dubbele paginaweergaven, sessies, aankopen en conversies, plus twee toestemmingsstatussen. |
Testen met Tag Assistant of | Geen van beide kijkt in de pixel-sandbox. |
| Breekt de signalen voor fraudepreventie zonder enig nalevingsvoordeel. |
| De standaard en de update spreken elkaar tegen. De update wint en het regiobeleid wordt genegeerd. |
Instellingen die dit artikel niet behandelt
De snippets laten Google's accountbrede instellingen weg, dus gelden Google's standaardwaarden:
url_passthrough | Of advertentieklik-ID's zoals GCLID tussen pagina's in de URL worden doorgegeven wanneer cookies niet kunnen worden geplaatstads_data_redaction | Of advertentieklik-ID's uit cookieloze pings worden verwijderd terwijl ad_storage is geweigerdallow_enhanced_conversions | Of gehashte klantgegevens mogen worden meegestuurd bij conversiesrestricted_data_processing | De beperking voor verkoop of delen in Amerikaanse staten, gerelateerd aan Shopify's saleOfDataAllowedwait_for_update | Hoe lang tags wachten op een toestemmingsupdate voordat ze afvurenPandectes kan hierover geen advies geven. De juiste waarden hangen af van je Google-accounts, de modellering waarop je vertrouwt, en de juridische positie van je bedrijf. Bespreek ze met degene die je Google-instellingen beheert, of met je juridisch adviseur als er een privacyvraag speelt.
Als je besluit er een in te stellen, voeg die dan toe als gtag("set", ...)-aanroep vóór de consent default-commando's:
// gtag("set", "url_passthrough", true);
// gtag("set", "ads_data_redaction", true);
wait_for_update vormt de uitzondering: die gaat binnen een default-commando. Deze snippets hebben het niet nodig, omdat init.customerPrivacy meteen beschikbaar is en de update direct na de standaardinstellingen wordt verstuurd.
Bijgewerkt op: 30/10/2026
Dankuwel!
