[ ENGINEERING_GUIDE ][ CONSENT_MODE ][ COOKIES ][ GA4 ][ GOOGLE_ADS ]

Consent Mode v2 — cookies, GA4 i Google Ads na stronie Next.js (2026)

10 czerwca 202610 min czytania
Autor: DevStudio.itStudio Web & AI

Google Consent Mode v2 krok po kroku: baner cookies, GA4 G-3HT7CZTN7P, tagi AW-, default denied vs granted, RODO i testowanie w Tag Assistant.

READ_TIME: 10 MIN_COMPLEXITY: MED_
STAMP: VERIFIED_BY_DS_

TL;DR

Od marca 2024 Google Consent Mode v2 jest wymagany dla ruchu z EOG/UK, jeśli używasz Google Ads lub GA4 z remarketingiem — inaczej tracisz modelowanie konwersji i część sygnałów do Smart Bidding. W Next.js wdrożenie to trzy warstwy: domyślny stan „denied” w gtag('consent', 'default', …) przed załadowaniem tagów, baner zapisujący wybór użytkownika (CookieBanner.tsx + localStorage), oraz gtag('consent', 'update', …) po akceptacji kategorii analitycznych/marketingowych. Na stronie DevStudio.it GA4 (G-3HT7CZTN7P) i trzy kontenery Ads (AW-17557280025, AW-17769880693, AW-18151506857) ładują się w layout.tsx — baner w [locale]/layout.tsx zapisuje preferencje, ale musi być połączony z Consent Mode, żeby wybór „Tylko niezbędne” realnie blokował sygnały reklamowe. Poniżej: implementacja, RODO, testy i typowe błędy.

Dla kogo to jest

  • Właścicieli stron firmowych z Google Ads i GA4 w UE/UK
  • Developerów Next.js integrujących CookieBanner z gtag
  • Osób odpowiedzialnych za RODO/GDPR i marketing performance jednocześnie
  • Zespołów widzących spadek danych konwersji po wdrożeniu banera cookies

Fraza (SEO)

consent mode v2 google, consent mode ga4 nextjs, baner cookies rodo google ads, default denied consent mode, testowanie consent mode tag assistant

Consent Mode to mechanizm Google, który przekazuje tagom (gtag, GTM) status zgody użytkownika na przechowywanie i odczyt cookies oraz wysyłanie danych do Google. Wersja v2 (obowiązkowa od 2024 dla Ads w EOG) rozszerza model o sygnały:

Parametr Znaczenie
analytics_storage Cookies analityczne (GA4)
ad_storage Cookies reklamowe
ad_user_data v2 — przesyłanie danych użytkownika do reklam Google
ad_personalization v2 — personalizacja reklam (remarketing)

Bez v2 Google może ograniczyć remarketing, konwersje modelowane i zgodność konta Ads z wymogami UE — nawet jeśli tagi technicznie „są na stronie”.

Consent Mode nie zastępuje banera cookies — to warstwa techniczna po decyzji użytkownika (lub przed nią w trybie default denied).

Architektura na stronie Next.js — stan obecny

W projekcie DevStudio.it:

Tagi w src/app/layout.tsx

  • GA4 G-3HT7CZTN7P — strategia afterInteractive (ważne dla generate_lead przy szybkim submit formularza)
  • Google Ads — trzy kontenery AW- ze strategią lazyOnload (LCP)
  • reCAPTCHA v3 — lazyOnload

GA4 config zawiera m.in. anonymize_ip: true i allow_ad_personalization_signals: false — to dobry punkt wyjścia pod RODO, ale nie zastępuje Consent Mode.

Baner w src/components/CookieBanner.tsx

Komponent kliencki ('use client') renderowany w src/app/[locale]/layout.tsx:

  • Po 2 s sprawdza localStorage.getItem('cookieConsent')
  • Zaakceptuj wszystkieanalytics: true, marketing: true
  • Tylko niezbędneanalytics: false, marketing: false
  • Preferencje w cookiePreferences jako JSON

To poprawny wzorzec UX (opóźnienie banera, trzy przyciski, link do polityki prywatności). Brakujący element produkcyjny: wywołanie gtag('consent', 'update', …) zsynchronizowane z przyciskami — bez tego localStorage i tagi Google żyją osobno.

Default denied vs granted — co wybrać

Default denied (zalecane w EOG)

Przed interakcją użytkownika wszystkie sygnały poza niezbędnymi są odmówione:

gtag('consent', 'default', {
  analytics_storage: 'denied',
  ad_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalization: 'denied',
  wait_for_update: 500,
});

Tagi mogą się załadować, ale wysyłają pingi bez cookies — Google modeluje część konwersji statystycznie. To zgodne z RODO, gdy baner informuje o cookies i daje realny wybór.

Default granted (ryzykowne w UE)

Wszystko „tak” do momentu kliknięcia „Odrzuć” — praktycznie nie spełnia standardu opt-in w większości interpretacji UODO/EDPB. Stosuj tylko poza EOG lub gdy prawnik wyraźnie zatwierdzi inną podstawę prawną.

wait_for_update

Parametr wait_for_update: 500 (ms) daje banerowi czas na ustawienie consent update przed pierwszym pełnym hitem — redukuje „flash” pełnego trackingu przed decyzją.

Implementacja krok po kroku w Next.js 15

W layout.tsx, przed gtag('config', 'G-...'):

<Script id="google-consent-default" strategy="beforeInteractive">
  {`
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('consent', 'default', {
      analytics_storage: 'denied',
      ad_storage: 'denied',
      ad_user_data: 'denied',
      ad_personalization: 'denied',
      wait_for_update: 500,
      region: ['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','IS','LI','NO','GB']
    });
  `}
</Script>

Tablica region ogranicza strict default do EOG+UK; poza tym regionem możesz ustawić granted (Google obsługuje to w jednym wywołaniu z mapą regionów — patrz dokumentacja gtag).

Krok 2: update w CookieBanner

Po handleAcceptAll:

const grantAll = () => {
  if (typeof window !== 'undefined' && window.gtag) {
    window.gtag('consent', 'update', {
      analytics_storage: 'granted',
      ad_storage: 'granted',
      ad_user_data: 'granted',
      ad_personalization: 'granted',
    });
  }
  localStorage.setItem('cookieConsent', 'accepted');
  localStorage.setItem('cookiePreferences', JSON.stringify({
    necessary: true, analytics: true, marketing: true,
  }));
  setIsVisible(false);
};

Po handleAcceptNecessaryupdate denied (lub tylko analytics_storage: 'denied' jeśli chcesz modelowanie Ads):

window.gtag?.('consent', 'update', {
  analytics_storage: 'denied',
  ad_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalization: 'denied',
});

Krok 3: Przywrócenie zgody po reload

W useEffect CookieBanner — jeśli cookieConsent === 'accepted', odczytaj cookiePreferences i wywołaj consent update zanim user zobaczy baner ponownie:

useEffect(() => {
  const consent = localStorage.getItem('cookieConsent');
  const prefsRaw = localStorage.getItem('cookiePreferences');
  if (consent && prefsRaw) {
    const prefs = JSON.parse(prefsRaw);
    window.gtag?.('consent', 'update', {
      analytics_storage: prefs.analytics ? 'granted' : 'denied',
      ad_storage: prefs.marketing ? 'granted' : 'denied',
      ad_user_data: prefs.marketing ? 'granted' : 'denied',
      ad_personalization: prefs.marketing ? 'granted' : 'denied',
    });
    return;
  }
  const timer = setTimeout(() => setIsVisible(true), 2000);
  return () => clearTimeout(timer);
}, []);

Krok 4: Lazy load Ads dopiero po marketing granted (opcjonalnie, agresywne RODO)

Alternatywa: nie ładuj skryptów AW- w layout wcale — inject dynamicznie po marketing: true. Maksymalna zgodność, ale utrudnia modelowanie i wymaga refaktoru layout.tsx. Consent Mode z default denied + loaded tags to kompromis akceptowany przez większość implementacji przy poprawnym banerze.

Przy analytics_storage: 'denied':

  • GA4 wysyła cookieless pings (consent mode modeling)
  • Raporty Realtime mogą pokazywać mniej sesji do czasu zgody
  • Zdarzenia generate_lead po submit formularza powinny trafiać po granted — testuj formularz po „Zaakceptuj wszystkie”

Przy afterInteractive GA4 ładuje się wcześnie — dlatego default denied musi być beforeInteractive, inaczej pierwszy page_view może wylecieć z pełnymi cookies.

Stała GA4_MEASUREMENT_ID w src/lib/ga4-measurement-id.ts musi pozostać zsynchronizowana z layout.tsx.

Projekt używa trzech tagów Ads (lazyOnload). Consent Mode dotyczy wszystkich — wspólny dataLayer i jedno consent update aktualizuje zachowanie każdego gtag('config', 'AW-...').

Po „Tylko niezbędne”:

  • Konwersje conversion_event_submit_lead_form mogą być modelowane, nie mierzone 1:1
  • Smart Bidding nadal dostaje sygnały aggregate, ale z opóźnieniem i niższą granularnością
  • Tag Assistant pokaże status consent per hit

Po „Zaakceptuj wszystkie” — pełne cookies _gcl_*, pełniejsza atrybucja.

Ważne: test konwersji Ads w incognito bez akceptacji marketingowej da inny wynik niż test po akceptacji — to nie bug, to RODO.

Wymóg prawny (uproszczenie) Baner CookieBanner Consent Mode
Informacja przed cookies Tekst + link do polityki Nie wystarczy sam
Wybór granularny „Dostosuj” z kategoriami Mapowanie kategorii → parametry gtag
Opt-in na marketing Przycisk „Tylko niezbędne” jako default action default denied
Dowód zgody localStorage + opcjonalnie log serwerowy Logi Google (suplement)

localStorage nie jest cookie — nadal dokumentuj w polityce prywatności, że preferencje są zapisywane lokalnie. Dla audytu rozważ endpoint zapisujący hash decyzji + timestamp (bez PII).

Kategorie w banerze:

  • Niezbędne — zawsze on (sesja, CSRF, reCAPTCHA może być „niezbędne” jeśli chroni formularz — skonsultuj z prawnikiem)
  • Analityczneanalytics_storage
  • Marketingowead_storage, ad_user_data, ad_personalization

Przycisk „Dostosuj” w obecnym UI pokazuje kategorie, ale toggle nie są interaktywne — to kolejny krok rozwoju: prawdziwe switch'e zapisujące częściową zgodę (analytics tak, marketing nie).

Tag Assistant Companion

  1. Otwórz stronę produkcyjną w incognito.
  2. Bez akceptacji cookies — wyślij formularz testowy.
  3. W Tag Assistant sprawdź Consent State — powinno być denied dla ad/analytics.
  4. Zaakceptuj wszystkie — odśwież — powtórz submit.
  5. Porównaj: GA4 Realtime, Ads konwersje (24–48 h opóźnienia).

Chrome DevTools → Application

  • Cookies — po denied brak _ga, _gcl_au (do czasu granted)
  • Local StoragecookieConsent, cookiePreferences

W Cele → Ustawienia → Consent mode status powinien być „Wykryto” po wdrożeniu default + update. „Nie wykryto” = tagi ignorują sygnały lub default jest za późno.

Checklist przed audytem RODO

  • consent default beforeInteractive, przed GA4 config
  • consent update na każdy przycisk banera
  • Przywrócenie zgody z localStorage po reload
  • Formularz + chatbot testowane w obu ścieżkach zgody
  • Polityka prywatności opisuje kategorie i Google jako procesor
  • Brak „ciemnych wzorców” (pre-checked marketing)

Wpływ na Core Web Vitals

beforeInteractive consent script to mały inline — minimalny wpływ na LCP. GA4 afterInteractive + Ads lazyOnload pozostaje dobrym układem po dodaniu Consent Mode — nie przenoś wszystkiego na beforeInteractive „dla pełnych danych”.

Modelowane konwersje przy denied nie wymagają pełnego ładowania wszystkich AW- przed zgodą — Google explicite projektuje ten trade-off.

Spójność: formularz, chatbot i wielojęzyczność

Lead może trafić z formularza kontaktowego (page.tsx/api/submissions) albo z chatbota (Chatbot.tsx/api/chatbot/submit). Oba wysyłają generate_lead i conversion_event_submit_lead_form po HTTP 200. Test Consent Mode musi obejmować obie ścieżki — inaczej marketing widzi „dziury” w konwersjach mimo poprawnego banera na stronie głównej.

Wersje /pl, /en, /de dzielą ten sam layout.tsx z tagami i ten sam CookieBanner w locale layout. Teksty banera są dziś po polsku niezależnie od locale — kolejny krok i18n to tłumaczenia przycisków i mapowanie linku polityki (/${locale}/polityka). Consent Mode nie zależy od języka — parametry granted/denied są uniwersalne.

Harmonogram wdrożenia (1–2 dni dev)

Dzień Zadanie Weryfikacja
1 rano consent default beforeInteractive w layout Tag Assistant: denied przed kliknięciem
1 popołudnie consent update w CookieBanner + restore z localStorage Reload strony zachowuje wybór
2 rano Test formularza + chatbota (denied / granted) GA4 Realtime, brak _ga przy denied
2 popołudnie Ads diagnostyka + aktualizacja polityki prywatności Panel Ads: Consent mode „Wykryto”

Po wdrożeniu odczekaj 48 h przed oceną kampanii Ads — modelowanie konwersji stabilizuje się dopiero po zebraniu próby ruchu w obu stanach zgody.

FAQ

Czy muszę używać GTM zamiast gtag.js?

Nie. Consent Mode działa z bezpośrednim gtag w layout.tsx tak samo jak z GTM. GTM ułatwia zarządzanie wieloma tagami bez deployu kodu — ale Next.js Script + stałe ID to valid pattern.

Czy „Tylko niezbędne” blokuje GA4 całkowicie?

Przy poprawnym Consent Mode GA4 nadal wysyła ograniczone pingi (modelowanie), ale bez cookies analitycznych. Raporty będą mniej kompletne do momentu granted. To oczekiwane.

Czy reCAPTCHA wymaga zgody marketingowej?

reCAPTCHA Google to osobna usługa — często klasyfikowana jako „niezbędna” do bezpieczeństwa formularza, ale przesyła dane do Google. UODO może wymagać wzmianki w polityce. Consent Mode nie kontroluje iframe reCAPTCHA — rozważ ładowanie skryptu dopiero przy focus na formularzu.

Co jeśli użytkownik wyczyści localStorage?

Baner pojawi się ponownie; default denied zadziała do następnej decyzji. To poprawne zachowanie.

Technicznie tak — przy default denied, update po zgodzie i banerze spełniającym RODO. Prawnie potrzebujesz też polityki prywatności, ewentualnie DPA z Google i dokumentacji procesów.

Dlaczego mam trzy tagi AW-?

Migracja kont reklamowych / kampanii historycznych. Consent Mode aktualizuje wspólny dataLayer — nie musisz trzy razy wywoływać update, wystarczy jedno gtag('consent', 'update', …).

Podsumowanie

Consent Mode v2 to most między RODO a skutecznością Google Ads/GA4: default denied w EOG, baner z realnym wyborem (jak CookieBanner.tsx), gtag consent update zsynchronizowany z localStorage, testy w Tag Assistant w obu ścieżkach zgody. Sam baner bez Consent Mode to połowa wdrożenia; same tagi bez banera to ryzyko prawne. Na Next.js 15 ustaw default beforeInteractive, zachowaj GA4 afterInteractive dla leadów i Ads lazyOnload dla wydajności — potem połącz przyciski banera z analytics_storage i parametrami ad_ v2.

O autorze

Budujemy szybkie strony WWW, aplikacje web/mobile, chatboty AI i hosting — z naciskiem na SEO i konwersję.

Przydatne linki

Od teorii do produkcji — Branchly, hosting, opieka i realizacje.

PODOBA CI SIĘ NASZA ARCHITEKTURA MYŚLENIA? ZBUDUJMY COŚ RAZEM.

[ ROZPOCZNIJ_KONFIGURACJĘ_PROJEKTU ]