Jeśli prowadzisz stronę sprzedażową lub pracujesz z danymi w eCommerce, to wdrożenie mechanizmu zarządzania zgodami (consent) jest dziś obowiązkiem zarówno prawnym, jak i praktycznym.
Ten artykuł pokaże Ci krok po kroku, jak bezpłatnie wdrożyć baner zgód na WordPressie i poprawnie połączyć go z Google Tag Manager – tak, aby Twoje tagi działały zgodnie z wyborem użytkownika.
Wdrożenie mechanizmu zarządzania zgodami to klucz do legalnej analityki – chroni prywatność i buduje zaufanie klientów.
Skrót (TL;DR)→ Zainstaluj darmowy baner zgód w WordPress i ustaw domyślny stan na denied.
→ W GTM wdroż Consent Mode, użyj zmiennych (opartych na ciasteczkach) i przypisz zgody do tagów.
→ Zweryfikuj swoje ustawienia dokonując testów.
Video tutorial
Kwestie prawne i ograniczenia
Zanim przejdziemy do technicznych działań, warto uświadomić sobie dwie rzeczy:
- Nie jestem prawnikiem – wskazówki techniczne w tym artykule nie zastąpią porady prawnej. Jeśli chcesz mieć pewność co do zgodności z lokalnymi przepisami (np. UODO w Polsce, RODO/ GDPR), skonsultuj się z prawnikiem.
- Płatne vs darmowe rozwiązania – na rynku istnieją gotowe, rozbudowane rozwiązania (Cookiebot, CookieYes, itp.), ale często są płatne w modelu abonamentowym. Wiele sklepów eCommerce może z powodzeniem skorzystać z darmowego rozwiązania, o ile rozumie jego ograniczenia (brak ewidencji zgód, brak automatycznego skanu ciasteczek, ręczne uzupełnianie polityki cookies itp.).

Darmowe rozwiązanie, które pokażę, jest wystarczające dla mniejszych eCommerce, które chcą uniknąć comiesięcznych opłat, ale rozumieją ryzyko i ograniczenia.

Jeśli prowadzisz duży serwis eCommerce z wieloma źródłami ruchu i intensywnymi kampaniami reklamowymi, rozważ jednak komercyjne rozwiązanie.
Wtyczki, które warto zainstalować
Polecam zainstalować dwie wtyczki do przeglądarki.
Cookie Editor – służy do przeglądania ciasteczek na stronie; bardzo pomocne przy testach i dokumentacji do polityki prywatności.

Consent Mode Inspector – pokazuje stan zgód: default state i update state (granted/denied). Przydaje się do szybkich testów poprawności wdrożenia.

Rozszerzenia w przeglądarce usprawnią cały proces testowania i debugowania.
Zrozumienie rodzajów zgód
Musisz wiedzieć, jakie rodzaje zgód będziesz obsługiwać, ponieważ w GTM przypisujemy rodzaje zgód do tagów.
Oto najważniejsze typy (terminologia zgodna z Google):
- analytics_storage – zgoda na zapisywanie danych analitycznych (np. GA4, statystyki odwiedzin)
- ad_storage – zgoda na zapisywanie danych reklamowych (np. remarketing w Google Ads).
- ad_user_data – zgoda na wysyłanie danych użytkownika do systemów reklamowych Google.
- ad_personalization – zgoda na personalizację reklam (dopasowanie treści reklam do użytkownika).
- personalization_storage – zgoda na zapisywanie danych do personalizacji treści/ustawień strony.
W praktyce podzielimy zgody na grupy:
- Niezbędne: functionality_storage (działanie strony).
- Analityczne: analytics_storage.
- Marketingowe / reklamowe: ad_storage, ad_user_data, ad_personalization.
- Personalizacyjne: personalization_storage.
Dzięki temu w Google Tag Manager będziemy mogli przypisać, które tagi mają się odpalać tylko przy danej zgodzie.
Dodanie darmowego bannera zgód na WordPress
W pierwszej kolejności zainstaluj wtyczkę na WordPress, do wklejania kodu w head/footer. Może to być wtyczka „Header Footer Code Manager”.

W panelu wtyczki dodaj nowy snippet (Add New Snippet). Nadaj mu nazwę, np. „Consent Mode”.

Wklej poniższy skrypt w sekcji „Snippet / Code”
Kod bannera
<style id="cb-styles">
/* ===================== STYLE (akcent: #1960BF) ===================== */
:root{ --cb-ico-scale:1.30; } /* powiększanie samej ikonki ciasteczka */
#cb-overlay{
position:fixed; inset:0; background:rgba(0,0,0,.45);
z-index:2147483647;
display:flex; align-items:center; justify-content:center;
}
#cb-modal{
background:#ffffff; color:#0f172a; width:min(720px,92vw);
border-radius:16px; border:1px solid #e5e7eb; box-shadow:0 24px 80px rgba(0,0,0,.25);
overflow:hidden; z-index:2147483647;
}
.cb-top{ padding:0 24px; border-bottom:1px solid #e5e7eb; display:flex; gap:28px; }
.cb-tab{ padding:16px 0 14px; border-bottom:3px solid transparent; cursor:pointer; font-weight:800; color:#0f172a; }
.cb-tab.active{ border-color:#1960BF; }
.cb-body{ padding:22px; max-height:60vh; overflow:auto; }
.cb-title{ margin:0 0 10px; font-size:28px; font-weight:800; }
.cb-desc{ margin:0 0 16px; color:#475569; font-size:16px; }
.cb-info{ font-size:16px; color:#0f172a; }
.cb-info a{ color:#1960BF; font-weight:800; text-decoration:none; border-bottom:2px solid #1960BF; padding-bottom:2px; }
.cb-list{ margin-top:8px; border-top:1px solid #e5e7eb; }
.cb-li{
display:flex; align-items:flex-start; justify-content:space-between;
padding:16px 0; border-bottom:1px solid #e5e7eb; gap:16px;
}
.cb-li strong{ font-weight:800; }
.cb-left{ min-width:0; }
.cb-sub{ margin:.35rem 0 0; color:#64748b; font-size:14px; line-height:1.4; }
.cb-right{ display:flex; align-items:center; gap:12px; flex:0 0 auto; }
/* Suwak */
.cb-switch{ position:relative; width:56px; height:32px; }
.cb-switch input{ display:none; }
.cb-slider{
position:absolute; inset:0; background:#e7e7e7; border-radius:999px; transition:.2s;
}
.cb-slider:before{
content:""; position:absolute; left:3px; top:3px; width:26px; height:26px;
background:#fff; border-radius:50%; transition:.2s; box-shadow:0 1px 2px rgba(0,0,0,.25);
}
.cb-switch input:checked + .cb-slider{ background:#1960BF; }
.cb-switch input:checked + .cb-slider:before{ transform:translateX(24px); }
.cb-switch.disabled input:checked + .cb-slider{ background:#4A80D6; }
.cb-switch.disabled{ opacity:.9; }
.cb-actions{
display:flex; gap:12px; flex-wrap:wrap; padding:16px 22px;
border-top:1px solid #e5e7eb; background:#fafafa;
margin-bottom:15px;
}
.cb-actions.center{ justify-content:center; }
.cb-actions.end{ justify-content:flex-end; }
.cb-btn{
border:0; border-radius:12px; padding:12px 18px; cursor:pointer;
font-weight:900; min-width:210px; text-align:center;
}
.cb-btn.primary{ background:#1960BF; color:#fff; }
.cb-btn.secondary{ background:#1960BF; color:#fff; }
.cb-btn.ghost{ background:#fff; border:1px solid #e5e7eb; color:#1960BF; }
@media(max-width:640px){ .cb-btn{ flex:1 1 100%; min-width:100%; } }
/* ===== LAUNCHER (ciasteczko) – stałe kółko, rośnie tylko ikona ===== */
#custom-button{
position:fixed; bottom:20px; left:20px; width:60px; height:60px; border:none; border-radius:50%;
background-color:#1960BF; cursor:pointer; z-index:2147483647;
display:flex; align-items:center; justify-content:center; transition:background-color .3s ease, transform .3s ease;
overflow:hidden; /* klip ikony gdy będzie większa */
}
#custom-button .cb-ico-wrap{ width:100%; height:100%; display:flex; align-items:center; justify-content:center; }
#custom-button .cb-ico{ width:36px; height:36px; display:block; transform:scale(var(--cb-ico-scale)); transform-origin:center; }
#custom-button:hover{ background-color:#144a94; transform:scale(1.06); }
</style>
<script>
(function(){
const COOKIE_NAME = "consent";
const TTL_DAYS = 180;
const UI = {
text: {
tabs:["Zgody","Szczegóły","Informacje"],
title:"Używam plików cookie",
desc:'Wykorzystuje pliki cookies do zapewnienia bezpieczeństwa, celów analitycznych i ulepszania naszej strony internetowej. Kliknij "Dostosuj", jeśli chcesz wybrać preferowane przez Ciebie pliki cookie.',
detailsIntro:"Dostosuj, które kategorie plików cookie mają być włączone:",
acceptAll:"Akceptuj wszystkie",
rejectAll:"Odrzuć wszystkie",
customize:"Dostosuj",
save:"Akceptuj wybrane",
infoHTML:'Więcej informacji znajdziesz w naszej <a href="/polityka-prywatnosci/" target="_blank" rel="nofollow">polityce prywatności</a>.'
},
detailsRows:[
["Funkcyjne (zawsze włączone)","functionality",true,"Pliki cookie konieczne do działania strony i nie przechowują danych pozwalających na identyfikację."],
["Analityczne","analytics",false,"Służą do zrozumienia zachowania użytkowników (np, liczba wizyt, źródła ruchu)."],
["Reklamowe","advertisement",false,"Służą do personalizacji reklam oraz analizy skuteczności kampanii."],
["Personalizacyjne","personalization",false,"Służą do dopasowania treści i interfejsu do użytkownika (np. zapamiętywanie preferencji, rekomendacje)."]
]
};
const KEYS = ["consent","functionality","analytics","advertisement","personalization"];
const DEFAULTS = {
consent:"no",
functionality:"yes",
analytics:"yes",
advertisement:"yes",
personalization:"yes"
};
function serializeState(state){
return KEYS.map(k => `${k}:${state[k]}`).join(",");
}
function setCookieState(state, days=TTL_DAYS){
const d = new Date(); d.setTime(d.getTime()+days*864e5);
const secure = location.protocol==="https:"?"; Secure":"";
document.cookie = `${COOKIE_NAME}=${serializeState(state)}; Expires=${d.toUTCString()}; Path=/; SameSite=Lax${secure}`;
}
function getCookieRaw(){
const m = document.cookie.split(";").map(c=>c.trim()).find(c=>c.startsWith(COOKIE_NAME+"="));
return m ? m.split("=",2)[1] : null;
}
function parseCookie(str){
const s = {...DEFAULTS};
if(!str) return s;
str.split(",").forEach(p=>{
const [k,v]=p.split(":");
if(k&&v&&KEYS.includes(k)) s[k]=(v==="yes"?"yes":"no");
});
s.functionality="yes";
return s;
}
const readState = () => parseCookie(getCookieRaw());
const hasChoice = () => readState().consent==="yes";
function pushDL(action, state){
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "button_consent_click",
action,
consent: {...state},
consent_raw: serializeState(state),
timestamp: new Date().toISOString()
});
}
function build(initial){
const ov=document.createElement("div");
ov.id="cb-overlay";
ov.innerHTML=`
<div id="cb-modal">
<div class="cb-top">
<div class="cb-tab active" data-tab="consents">${UI.text.tabs[0]}</div>
<div class="cb-tab" data-tab="details">${UI.text.tabs[1]}</div>
<div class="cb-tab" data-tab="info">${UI.text.tabs[2]}</div>
</div>
<div class="cb-body">
<div id="pane-consents">
<h3 class="cb-title">${UI.text.title}</h3>
<p class="cb-desc">${UI.text.desc}</p>
</div>
<div id="pane-details" style="display:none">
<p class="cb-desc">${UI.text.detailsIntro}</p>
<div class="cb-list">
${UI.detailsRows.map(([label,key,lock,desc])=>`
<div class="cb-li">
<div class="cb-left">
<strong>${label}</strong>
${desc?`<p class="cb-sub">${desc}</p>`:""}
</div>
<div class="cb-right">
<label class="cb-switch ${lock?"disabled":""}">
<input type="checkbox" id="cb_${key}" ${initial[key]==="yes"?"checked":""} ${lock?"disabled":""}>
<span class="cb-slider"></span>
</label>
</div>
</div>`).join("")}
</div>
</div>
<div id="pane-info" class="cb-info" style="display:none">${UI.text.infoHTML}</div>
</div>
<div class="cb-actions center" id="actions-consents">
<button class="cb-btn ghost" id="btn-reject">${UI.text.rejectAll}</button>
<button class="cb-btn ghost" id="btn-customize">${UI.text.customize}</button>
<button class="cb-btn primary" id="btn-accept">${UI.text.acceptAll}</button>
</div>
<div class="cb-actions end" id="actions-details" style="display:none">
<button class="cb-btn primary" id="btn-save">${UI.text.save}</button>
</div>
</div>`;
document.body.appendChild(ov);
const tabs=ov.querySelectorAll(".cb-tab");
const panes={consents:ov.querySelector("#pane-consents"),details:ov.querySelector("#pane-details"),info:ov.querySelector("#pane-info")};
const acts={consents:ov.querySelector("#actions-consents"),details:ov.querySelector("#actions-details")};
function showTab(which){
tabs.forEach(x=>x.classList.remove("active"));
[...tabs].find(t=>t.dataset.tab===which).classList.add("active");
Object.keys(panes).forEach(k=>panes[k].style.display = k===which ? "block" : "none");
acts.consents.style.display = which==="consents" ? "flex":"none";
acts.details.style.display = which==="details" ? "flex":"none";
}
tabs.forEach(t=>t.addEventListener("click",()=>showTab(t.dataset.tab)));
ov.querySelector("#btn-accept").addEventListener("click",()=>{
const s={consent:"yes",functionality:"yes",analytics:"yes",advertisement:"yes",personalization:"yes"};
setCookieState(s);
ov.remove();
setTimeout(()=>pushDL("accept_all", s),500);
});
ov.querySelector("#btn-reject").addEventListener("click",()=>{
const s={consent:"yes",functionality:"yes",analytics:"no",advertisement:"no",personalization:"no"};
setCookieState(s);
ov.remove();
setTimeout(()=>pushDL("reject_all", s),500);
});
ov.querySelector("#btn-customize").addEventListener("click",()=>showTab("details"));
ov.querySelector("#btn-save").addEventListener("click",()=>{
const val=k=>(ov.querySelector("#cb_"+k)?.checked?"yes":"no");
const s={
consent:"yes",
functionality:"yes",
analytics:val("analytics"),
advertisement:val("advertisement"),
personalization:val("personalization")
};
setCookieState(s);
ov.remove();
setTimeout(()=>pushDL("accept_selected", s),500);
});
}
function addLauncher(){
if(document.getElementById("custom-button")) return;
const btn=document.createElement("button");
btn.id="custom-button"; btn.setAttribute("aria-label","Zmień zgody");
/* to SAMO ciasteczko – tylko opakowane, by można było je skalować niezależnie od kółka */
btn.innerHTML=`<span class="cb-ico-wrap">
<svg class="cb-ico" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false">
<path fill="#ffffff" d="M257.5 27.6c-.8-5.4-4.9-9.8-10.3-10.6c-22.1-3.1-44.6.9-64.4 11.4l-74 39.5C89.1 78.4 73.2 94.9 63.4 115L26.7 190.6c-9.8 20.1-13 42.9-9.1 64.9l14.5 82.8c3.9 22.1 14.6 42.3 30.7 57.9l60.3 58.4c16.1 15.6 36.6 25.6 58.7 28.7l83 11.7c22.1 3.1 44.6-.9 64.4-11.4l74-39.5c19.7-10.5 35.6-27 45.4-47.2l36.7-75.5c9.8-20.1 13-42.9 9.1-64.9c-.9-5.3-5.3-9.3-10.6-10.1c-51.5-8.2-92.8-47.1-104.5-97.4c-1.8-7.6-8-13.4-15.7-14.6c-54.6-8.7-97.7-52-106.2-106.8zM208 144a32 32 0 1 1 0 64 32 32 0 1 1 0-64zM144 336a32 32 0 1 1 64 0 32 32 0 1 1 0-64zm224-64a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>
</svg>
</span>`;
btn.addEventListener("click",()=>window.cookieBanner.open());
document.body.appendChild(btn);
}
window.cookieBanner={
open:()=>build(readState()),
reset:()=>{ setCookieState({...DEFAULTS,consent:"no"}); build(readState()); },
get:()=>readState(),
getRaw:()=>getCookieRaw()
};
window.addEventListener("load",()=>{
addLauncher();
if(!hasChoice()) build(readState());
});
})();
</script>Zapisz snippet i opublikuj. Przejdź na stronę i sprawdź, czy pojawiła się ikona ciasteczka i baner.

Baner, który proponuję, jest minimalistyczny, ale funkcjonalny: ma opcję „Akceptuj wszystkie”, „Odrzuć wszystkie” oraz „Dostosuj” – gdzie użytkownik może włączyć / wyłączyć grupy zgód (functionality, analytics, advertisement, personalization).

Użytkownik może zapisać wybór w ciasteczku „consent”, którego wartość zawiera tekstowy zapis stanu zgód (yes/no dla każdej grupy). To właśnie będziemy potem zaczytywać w GTM.

Ustawienia w Google Tag Manager
Teraz pora na połączenie bannera z tagami śledzącymi. Do tego celu wykorzystamy Google Tag Managera.
Dodanie szablonu Consent Mode
W GTM przejdź do zakładki „Szablony” (Templates) – wybierz „Galeria”. Wyszukaj „Consent Mode” (autor: Simo Ahava) i dodaj go do obszaru roboczego.

Utwórz dwa tagi:
- consent mode – default: będzie uruchamiany przy załadowaniu kontenera (stan początkowy – deny)
- consent mode – update: będzie uruchamiany, gdy zmieni się zgoda użytkownika (np. kliknięcie w baner).
Ustaw regułę dla tagu default: „Consent Initialization – All Pages” (czyli ma działać przy każdej wczytanej stronie).

Skopiuj tag i utwórz wersję update, do której później dopiszesz regułę uruchamiania przy zmianie zgody.

Sprawdz działanie tagów. Ustaw w Consent Mode – update wszystko na „granted”.

Następnie wejdź w tryb poglądu Google Tag Managera i sprawdź co pokazała wtyczka.

Włączenie opcji uzyskiwania zgód w ustawieniach kontenera
Aby móc przypisywać zgody do tagów, w ustawieniach kontenera w GTM musisz włączyć przegląd ustawień uzyskania zgody.
Przejdź do Administracja (Admin) → Ustawienia kontenera (Container Settings). W dolnej części włącz „Włącz przegląd ustawień uzyskania zgody (beta)„. Zapisz zmiany.

Po tej zmianie w widoku tagu zobaczysz ikonę tarczy, która umożliwia przypisywanie typów zgód (wbudowane / dodatkowe). Dzięki temu GTM blokuje lub pozwala na odpalenie tagu w zależności od zgody użytkownika.

Ustawienie zmiennych, które odczytują ciasteczko consent
Ciasteczko „consent” zawiera informacje w formacie tekstowym (np. analytics=yes, advertisement=no, personalization=yes).
Musimy w GTM przygotować zmienne, które odczytają poszczególne wartości i zwrócą „granted” lub „denied” (to są wartości akceptowane przez tag consent mod).
W GTM wejdź w Zmienne -> Nowa zmienna zdefiniowana przez użytkownika. Wybierz typ „Niestandardowy kod JavaScript (Custom JavaScript)”.

Wklej fragment kodu JavaScript, który odczytuje ciasteczko consent i sprawdza np. wartość dla „advertisement”. Jeśli wartość to „yes”, zwróć „granted”, jeśli „no” – „denied”.
Kod zmiennej dla reklam (advertisement)
function () {
function getConsentCookie() {
var name = 'consent='; // Nazwa ciasteczka ze zrzutu ekranu
var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków specjalnych
var parts = decodedCookie.split(';');
for (var i = 0; i < parts.length; i++) {
var c = parts[i];
// Usuwanie spacji wiodących (alternatywa dla pętli while)
c = c.trim();
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return '';
}
var v = getConsentCookie();
// Sprawdzamy zgodę
if (v.indexOf('advertisement:yes') > -1) {
return 'granted';
}
// Sprawdzamy odmowę
if (v.indexOf('advertisement:no') > -1) {
return 'denied';
}
// Domyślna wartość
return 'denied';
}Kod zmiennej dla analityki (analytics)
function () {
function getConsentCookie() {
var name = 'consent='; // Nazwa ciasteczka ze zrzutu ekranu
var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków specjalnych
var parts = decodedCookie.split(';');
for (var i = 0; i < parts.length; i++) {
var c = parts[i];
// Usuwanie spacji wiodących (alternatywa dla pętli while)
c = c.trim();
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return '';
}
var v = getConsentCookie();
// Sprawdzamy zgodę
if (v.indexOf('analytics:yes') > -1) {
return 'granted';
}
// Sprawdzamy odmowę
if (v.indexOf('analytics:no') > -1) {
return 'denied';
}
// Domyślna wartość
return 'denied';
}Kod zmiennej dla funkcjnych (functionality)
function () {
function getConsentCookie() {
var nameA = 'consent=';
var nameB = 'cookieyes-consent=';
var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków
var parts = decodedCookie.split(';');
for (var i = 0; i < parts.length; i++) {
var c = parts[i].trim();
if (c.indexOf(nameA) === 0) return c.substring(nameA.length, c.length);
if (c.indexOf(nameB) === 0) return c.substring(nameB.length, c.length);
}
return '';
}
var v = getConsentCookie();
// Sprawdzamy zgodę
if (v.indexOf('functionality:yes') > -1) {
return 'granted';
}
// Sprawdzamy odmowę
if (v.indexOf('functionality:no') > -1) {
return 'denied';
}
// Domyślna wartość
return 'denied';
}Kod zmiennej dla personaliacji (personalization)
function () {
function getConsentCookie() {
var nameA = 'consent=';
var nameB = 'cookieyes-consent=';
var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków
var parts = decodedCookie.split(';');
for (var i = 0; i < parts.length; i++) {
var c = parts[i].trim();
if (c.indexOf(nameA) === 0) return c.substring(nameA.length, c.length);
if (c.indexOf(nameB) === 0) return c.substring(nameB.length, c.length);
}
return '';
}
var v = getConsentCookie();
// Sprawdzamy zgodę
if (v.indexOf('personalization:yes') > -1) {
return 'granted';
}
// Sprawdzamy odmowę
if (v.indexOf('personalization:no') > -1) {
return 'denied';
}
// Domyślna wartość
return 'denied';
}Nazwij zmienne w następujący sposób:
- cjs – cookie-advertisement-status,
- cjs – cookie-analytics-status,
- cjs – cookie-functionality-status.
- cjs – cookie-personalization-status.


Dzięki nim będziemy mogli dynamicznie ustawiać stan zgód w tagu consent update (w miejscu, gdzie w GTM można wskazać zmienną zamiast stałej wartości).

Połączenie zmiennych z tagiem Consent Mode – update
Wróć do tagu „consent mode – update” i w jego ustawieniach zmień wartości poszczególnych zgód tak, aby odczytywały nasze zmienne CJS. Przykład:
- ad_storage – zmienna „cjs – cookie-advertisement-status”,
- ad_user_data – zmienna „cjs – cookie-advertisement-status”,
- analytics_storage – zmienna „cjs – cookie-analytics-status”,
- ad_personalization – zmienna „cjs – cookie-personalization-status”.

Dla „Other signals” można zostawić zgody na „granted”.
W ten sposób tag consent update będzie się dynamicznie dostosowywał do wyboru użytkownika: jeśli ciasteczko pokaże „yes” dla analytics, GTM ustawi analytics jako granted; jeśli „no”, to denied.
Uwaga: Default zawsze powinien być ustawiony na denied (czerwony). Tylko update ma brać wartość z ciasteczek. Dzięki temu mamy zasadę „privacy by default”.

Reguły wyzwalania update – reagowanie na kliknięcie w banner
Musisz zadbać o dwa scenariusze:
- Użytkownik pierwszy raz wchodzi na stronę – baner pojawia się i mamy default = denied; po akceptacji powinien uruchomić się update.
- Użytkownik zmienia zdanie podczas sesji – kliknięcie w baner powinno zaktualizować stan zgód (update).
Aby to zrobić, w pierwszej kolejności w trybie podglądu GTM sprawdź, jakie eventy generuje baner. W przykładzie jest to event typu custom: button_consent_click.

W GTM utwórz nową regułę (Trigger) typu „Zdarzenie niestandardowe (Custom Event)” i jako nazwę wpisz dokładnie nazwę eventu wygenerowaną przez baner.

Przypisz tę regułę jako dodatkowy wyzwalacz do tagu „Consent Mode – Update”. Dzięki temu tag update będzie wywoływany zarówno przy załadowaniu strony, jak i przy kliknięciu przez użytkownika.

Teraz gdy użytkownik kliknie „Akceptuj wszystkie”, wygenerowany event uruchomi tag update oraz od razu ustawi odpowiednie zgody w GTM.

Przypisywanie zgód do tagów
W sekcji tagów, kliknij w ikonkę tarczy i przejdź do „Przgląd ustawień uzyskiwania zgody”.

Następnie:
- Dla tagów związanych z Consent Mode – ustaw „Brak wymogu uzyskania dodatkowej zgody”, ponieważ są one za to odpowiedzialne,
- Dla tagów Google – ustaw „Brak wymogu uzyskania dodatkowej zgody”, ponieważ tagi Google mają wbudowaną obsługę consent,
- Dla tagów niegooglowskich – wybierz opcję „Dodatkowa zgoda na uruchomienie tagu” i dodaj wymagane typy zgód (ad_storage, analytics, personalization w zależności od tagu).

Po tej czynności, wszystkie tagi powinny znajdować się w sekcji „Skonfigurowano uzyskiwania zgody”.

Teraz jeśli użytkownik nie wyraził zgody na reklamy, Facebook Pixel nie będzie uruchomiony.
Dla Google Analytics natomiast, jeśli zgoda na analytics nie została dana, GTM wyśle zanonimizowane pingi (advanced consent mode), które pozwalają Google modelować dane bez pełnej identyfikacji – to legalne i użyteczne, ale trzeba o tym pamiętać przy interpretacji danych eCommerce.

Zdarzenie page_view dla tagów niegooglowskich
Jeden problem, który pojawił się w praktyce: gdy użytkownik wchodzi pierwszy raz na stronę i akceptuje baner, page_view dla Facebooka może nie zostać wysłany, ponieważ tag próbował się uruchomić jeszcze przed zarejestrowaniem updatu zgody.
Aby to rozwiązać, w tagu „consent mode – update” włącz opcję „Push Data Layer Event” – czyli po wykonaniu update tag wygeneruje event w warstwie danych, np. gtm_constant_update.

W GTM utwórz trigger (regułę) typu Custom Event z nazwą dokładnie taką jak wygenerowany event (gtm_constant_update).

Dla tagów niegooglowskich, które wyświetlają się na każdej podstronie (np. FB Pixel – PageView) zmień regułę odpalenia: zamiast All Pages użyj tego custom eventu.
W ten sposób page_view dla Facebooka będzie wysłany dopiero po zaktualizowaniu zgód – czyli po akceptacji przez użytkownika.

W efekcie: kolejność działań jest następująca:
- użytkownik akceptuje zgodę,
- tag consent update uruchamia się i pushuje event „gtm_constant_update”,
- tag FB Pixel – PageView nasłuchuje eventu i dopiero wtedy się odpala.

Dzięki temu nie zgubimy PageView i jednocześnie nie złamiemy preferencji użytkownika.
Sprawdzenie i testowanie rozwiązań
Poniżej podsumowująca lista kontrolna, którą warto na końcu sprawdzić:
- Baner wyświetla się poprawnie na wszystkich typach urządzeń.
- Domyślny stan (default) zgód to denied.
- Wykonano testy: akceptuj wszystkie, odrzuć wszystkie, akceptuj wybrane i zmiana decyzji w trakcie sesji.
- Po akceptacji ciasteczko consent przyjmuje wartości yes dla odpowiednich grup.
- Tag consent update odczytuje wartości ciasteczka przez zmienne z Google Tag Manager i ustawia granted/denied.
- Tagi Google (np. Google Analytics, Google Ads) działają z consent mode (anonimowe pingi, gdy brak zgody).
- Tagi niegooglowskie typu wyświetlenie strony (np. Facebook PageView) odpalają się dopiero po gtm_constant_update (jeśli użytkownik zaakceptował).
- Wszystkie zdarzenia eCommerce (add_to_cart, purchase) są wysyłane zgodnie z oczekiwaniami i state zgód.
Użyj rozszerzeń Cookie Editor i Consent Mode Inspector do szybkiego podglądu stanu zgód i wartości ciasteczek. Sprawdzaj też DebugView w Google Analytics (tryb debugowania) dla weryfikacji wysyłanych zdarzeń.

Publikacja kontenera i monitorowanie
Gdy jesteś pewien, że wszystko działa poprawnie w trybie podglądu i testy przechodzą pomyślnie, opublikuj kontener Google Tag Managera.

Po publikacji monitoruj dane w Google Analytics i narzędziach reklamowych przez kolejne dni. Sprawdź, czy modelowane dane (w GA) są zgodne z oczekiwaniami oraz czy konwersje eCommerce nadal są raportowane poprawnie.
Podsumowanie
Wdrożenie consent mode na stronie WordPress i integracja z Google Tag Manager to proces techniczny, który wymaga uwagi i testów. Dobre wdrożenie mechanizmu zgód to inwestycja, która minimalizuje ryzyko problemów prawnych i organizacyjnych.
Najczęściej zadawane pytania (FAQ)
Czy wdrożenie Consent Mode na WordPressie wymaga płatnych wtyczek typu Cookiebot?
Nie, skuteczne wdrożenie mechanizmu zgód można zrealizować bez miesięcznych abonamentów, wykorzystując niestandardowe skrypty i GTM. Płatne narzędzia oferują wygodę, ale darmowe rozwiązania dają pełną kontrolę i oszczędność, o ile są poprawnie skonfigurowane technicznie.
Podejmując współpracę ze mną, wdrażam dedykowane, bezpłatne rozwiązanie oparte na kodzie, które uwalnia Cię od stałych kosztów licencyjnych.
LSI: darmowy banner cookies, wtyczki WordPress, Cookiebot alternatywa, skrypt Java Script, oszczędność budżetu
Dlaczego wdrożenie Consent Mode v2 jest krytyczne dla analityki eCommerce?
Bez Consent Mode Google blokuje ciasteczka reklamowe i analityczne użytkowników odrzucających zgody, co powoduje drastyczne luki w danych. Wdrożenie tego trybu pozwala na legalne przesyłanie sygnałów (pingów) bez danych osobowych, co umożliwia modelowanie brakujących konwersji.
W ramach współpracy ze mną konfiguruję tryb Advanced, abyś odzyskiwał dane o sprzedaży dzięki modelowaniu behawioralnemu.
LSI: Consent Mode v2, modelowanie danych, luki w analityce, RODO, utrata konwersji, pingi bez cookies
Jak poprawnie skonfigurować tagi Consent Mode w Google Tag Manager?
Kluczem jest rozdzielenie inicjalizacji na dwa etapy: „Default” (domyślna blokada) oraz „Update” (aktualizacja po decyzji). Błąd w kolejności ładowania tych tagów sprawia, że strona albo nie zbiera danych wcale, albo łamie prawo, uruchamiając śledzenie przed zgodą.
Korzystając z mojej pomocy, otrzymujesz bezbłędną strukturę tagów zapewniającą zasadę „privacy by default” przy zachowaniu ciągłości danych.
LSI: tag inicjalizacji, Consent Initialization, tag Update, privacy by default, sekwencja tagów GTM
W jaki sposób GTM odczytuje decyzje użytkownika z niestandardowego bannera?
GTM nie „widzi” bannera automatycznie; potrzebuje zmiennych JavaScript, które tłumaczą tekstowe wartości ciasteczka (np. „marketing:yes”) na standard zrozumiały dla Google („granted”). Bez tego „tłumacza” system nie wie, które tagi uruchomić.
Współpracując ze mną, przygotowuję precyzyjne skrypty parsujące ciasteczka, które eliminują błędy interpretacji zgód.
LSI: zmienne niestandardowe JS, parsowanie cookies, status granted denied, mapowanie zgód, ad_storage
Czym różni się konfiguracja tagów Google od tagów Facebooka w kontekście zgód?
Tagi Google (GA4, Ads) mają wbudowaną logikę Consent Mode i same dostosowują swoje zachowanie. Tagi podmiotów trzecich, jak Meta Pixel czy LinkedIn Insight Tag, nie mają tej funkcji – musisz ręcznie zablokować ich uruchomienie w GTM do momentu uzyskania konkretnej zgody.
Podejmując współpracę ze mną, przeprowadzam audyt wszystkich tagów, aby Meta Pixel nie naruszał RODO i nie narażał Cię na kary.
LSI: Additional Consent Checks, Meta Pixel RODO, blokowanie tagów, analityka podmiotów trzecich, ad_personalization
Dlaczego Facebook Pixel nie zlicza PageView przy pierwszym wejściu na stronę?
To tzw. „race condition” – tag Pixela próbuje odpalić się szybciej, niż GTM zdąży przetworzyć zgodę użytkownika. Rozwiązaniem jest opóźnienie tagu PageView i wyzwolenie go dopiero po zdarzeniu aktualizacji zgód (custom event), a nie na standardowe „All Pages”.
W ramach współpracy ze mną tworzę szczelną sekwencję zdarzeń, która eliminuje utratę danych przy pierwszym kontakcie z marką.
LSI: race condition, gtm_constant_update, utrata danych PageView, sekwencjonowanie zdarzeń, Data Layer push
Jak zweryfikować, czy wdrożenie Consent Mode działa poprawnie?
Samo zniknięcie bannera to za mało. Należy sprawdzić w konsoli developera lub trybie podglądu GTM, czy po odrzuceniu zgód statusy „ad_storage” i „analytics_storage” faktycznie zmieniają się na „denied”. Częstym błędem jest wizualna iluzja działania przy braku faktycznej blokady.
Korzystając z mojej pomocy, otrzymujesz raport weryfikacyjny z narzędzi debugujących, potwierdzający szczelność i legalność systemu.
LSI: debugowanie GTM, Consent Mode Inspector, weryfikacja wdrożenia, audyt analityki, statusy ciasteczek

