🢂Implementacja zgody (RODO) i cookies dla gtag.js (GA4)

Zarządzanie zgodą użytkownika i zapisem cookies w Google Analytics

Ten fragment kodu ustawia domyślnie brak zgody na przetwarzanie danych, tworzy lekki „shim” gtag() przed załadowaniem GA4 oraz zapisuje wybór użytkownika w cookie. Po akceptacji lub odmowie zgody aktualizuje status w gtag('consent', ...), dzięki czemu Analytics i funkcje reklamowe działają wyłącznie zgodnie z wyborem użytkownika.

Krótki opis

  • Naprawa błędu setCookie is not defined przez dodanie własnej, prostej implementacji.
  • Rozdzielenie logiki: domyślnie denied, a „granted” dopiero po świadomej akceptacji (np. klik w baner).
  • Przechowywanie wyboru w cookie (SameSite=Lax, Secure przy HTTPS) i jego odtwarzanie przy kolejnych wizytach.
  • Zgodność z GA4: użycie gtag('consent', 'default'|'update', {...}) dla poszczególnych obszarów zgód.
 <script>
  // --- utils: cookies ---
  function setCookie(name, value, days) {
    var expires = "";
    if (typeof days === "number") {
      var d = new Date();
      d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
      expires = "; expires=" + d.toUTCString();
    }
    var secure = location.protocol === "https:" ? "; Secure" : "";
    document.cookie =
      encodeURIComponent(name) + "=" + encodeURIComponent(value) +
      expires + "; path=/; SameSite=Lax" + secure;
  }

  function getCookie(name) {
    var n = encodeURIComponent(name) + "=";
    var parts = document.cookie.split("; ");
    for (var i = 0; i < parts.length; i++) {
      if (parts[i].indexOf(n) === 0) return decodeURIComponent(parts[i].slice(n.length));
    }
    return "";
  }

  // --- gtag shim + domyślna zgoda: DENIED ---
  window.dataLayer = window.dataLayer || [];
  function gtag(){ dataLayer.push(arguments); }

  var CONSENT_DENIED = {
    analytics_storage: 'denied',
    display_features: 'denied',
    personalization_storage: 'denied',
    security_storage: 'denied',
    functionality_storage: 'denied',
    ad_personalization: 'denied',
    ad_storage: 'denied',
    ad_personalization_signals: 'denied',
    ad_user_data: 'denied'
  };

  var CONSENT_GRANTED = {
    analytics_storage: 'granted',
    display_features: 'granted',
    personalization_storage: 'granted',
    security_storage: 'granted',
    functionality_storage: 'granted',
    ad_personalization: 'granted',
    ad_storage: 'granted',
    ad_personalization_signals: 'granted',
    ad_user_data: 'granted'
  };

  // Przy starcie ustawiamy domyślnie DENIED (zgodnie z RODO)
  gtag('consent', 'default', CONSENT_DENIED);
  setCookie('consent', JSON.stringify(CONSENT_DENIED), 180);

  // Jeśli masz zapisany wybór z poprzedniej wizyty, zastosuj go:
  (function restoreSavedConsent(){
    try {
      var saved = getCookie('consent');
      if (saved) {
        var obj = JSON.parse(saved);
        gtag('consent', 'update', obj);
      }
    } catch(e) {}
  })();

  // Wywołaj to dopiero po zgodzie użytkownika (np. handler przycisku „Akceptuję”)
  function grantConsent(){
    gtag('consent', 'update', CONSENT_GRANTED);
    setCookie('consent', JSON.stringify(CONSENT_GRANTED), 180);
  }

  // Ewentualnie handler odmowy
  function denyConsent(){
    gtag('consent', 'update', CONSENT_DENIED);
    setCookie('consent', JSON.stringify(CONSENT_DENIED), 180);
  }
</script>

Jak to działa:

Pierwsza wizyta

  • Na starcie ustawiasz default = denied.
  • Dopiero po świadomej akceptacji wywołujesz grantConsent(), co ustawia granted. Bez akcji użytkownika pozostaje denied.

Ta sama wizyta po kliknięciu

  • Po kliknięciu „Akceptuję” wywołujesz grantConsent() i od razu masz granted.

Kolejne wizyty

  • Skrypt na początku ustawia default = denied, a następnie funkcja odtwarzająca wybór z ciasteczka natychmiast robi gtag('consent','update', ...) zgodnie z zapisanym wyborem. Jeśli w ciastku było granted, to praktycznie od razu po starcie masz granted.

Przykład podpięcia pod przyciski w banerze:

<button onclick="grantConsent()">Akceptuję</button>
<button onclick="denyConsent()">Odmawiam</button>

Kompletna implementacja

<!-- Baner zgody (przykład) -->
<div id="cookie-banner" style="position:fixed;bottom:0;left:0;right:0;padding:12px;background:#f3f3f3;border-top:1px solid #ddd;">
  Używamy plików cookie do analityki. Zgadzasz się?
  <button data-consent="accept">Akceptuję</button>
  <button data-consent="reject">Odrzucam</button>
</div>

<script>
  // Opcjonalnie: Twój identyfikator GA4. Jeśli nie chcesz autoładować GA po zgodzie, usuń GA_MEASUREMENT_ID i loadGA.
  const GA_MEASUREMENT_ID = "G-XXXXXXXXXX";

  // --- utils: cookies ---
  function setCookie(name, value, days) {
    var expires = "";
    if (typeof days === "number") {
      var d = new Date(); d.setTime(d.getTime() + days * 864e5);
      expires = "; expires=" + d.toUTCString();
    }
    var secure = location.protocol === "https:" ? "; Secure" : "";
    document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) +
      expires + "; path=/; SameSite=Lax" + secure;
  }
  function getCookie(name) {
    var n = encodeURIComponent(name) + "=";
    var parts = document.cookie.split("; ");
    for (var i = 0; i < parts.length; i++) if (parts[i].indexOf(n) === 0)
      return decodeURIComponent(parts[i].slice(n.length));
    return "";
  }

  // --- gtag shim + domyślna zgoda: DENIED ---
  window.dataLayer = window.dataLayer || [];
  function gtag(){ dataLayer.push(arguments); }

  var CONSENT_DENIED = {
    analytics_storage: 'denied',
    display_features: 'denied',
    personalization_storage: 'denied',
    security_storage: 'denied',
    functionality_storage: 'denied',
    ad_personalization: 'denied',
    ad_storage: 'denied',
    ad_personalization_signals: 'denied',
    ad_user_data: 'denied'
  };
  var CONSENT_GRANTED = {
    analytics_storage: 'granted',
    display_features: 'granted',
    personalization_storage: 'granted',
    security_storage: 'granted',
    functionality_storage: 'granted',
    ad_personalization: 'granted',
    ad_storage: 'granted',
    ad_personalization_signals: 'granted',
    ad_user_data: 'granted'
  };

  gtag('consent', 'default', CONSENT_DENIED);
  setCookie('consent', JSON.stringify(CONSENT_DENIED), 180);

  // Opcjonalnie: ładuj GA dopiero po zgodzie
  function loadGA(id) {
    if (!id || window.__gaLoaded) return;
    var s = document.createElement('script');
    s.async = true; s.src = 'https://www.googletagmanager.com/gtag/js?id=' + id;
    s.onload = function() {
      gtag('js', new Date());
      gtag('config', id);
      window.__gaLoaded = true;
    };
    document.head.appendChild(s);
  }

  // Funkcje publiczne
  function grantConsent(){
    gtag('consent', 'update', CONSENT_GRANTED);
    setCookie('consent', JSON.stringify(CONSENT_GRANTED), 180);
    loadGA(GA_MEASUREMENT_ID); // usuń tę linię, jeśli GA ma być ładowane zawsze
    hideBanner();
  }
  function denyConsent(){
    gtag('consent', 'update', CONSENT_DENIED);
    setCookie('consent', JSON.stringify(CONSENT_DENIED), 180);
    hideBanner();
  }
  function hideBanner(){
    var b = document.getElementById('cookie-banner');
    if (b && b.parentNode) b.parentNode.removeChild(b);
  }

  // Odtwórz wybór z poprzedniej wizyty
  (function restoreSavedConsent(){
    try {
      var saved = getCookie('consent');
      if (saved) {
        var obj = JSON.parse(saved);
        gtag('consent', 'update', obj);
        if (obj.analytics_storage === 'granted') loadGA(GA_MEASUREMENT_ID);
        // Jeśli zgoda była już udzielona, możesz od razu schować baner:
        if (obj.analytics_storage === 'granted') hideBanner();
      }
    } catch(e) {}
  })();

  // >>> TU DODAJEMY WYWOŁANIE grantConsent() PO KLIKNIĘCIU <<<
  // Obsługa przycisków w banerze
  document.querySelectorAll('[data-consent="accept"]').forEach(function (btn) {
    btn.addEventListener('click', grantConsent);   // <-- wywołanie grantConsent()
  });
  document.querySelectorAll('[data-consent="reject"]').forEach(function (btn) {
    btn.addEventListener('click', denyConsent);
  });

  // Opcja alternatywna: wywołaj z inline HTML, bo funkcje są w globalnym scope:
  // <button onclick="grantConsent()">Akceptuję</button>
</script>

Przykład użycia - Najważniejsze elementy:

  • grantConsent()wywoływane jest bezpośrednio w handlerze kliknięcia „Akceptuję”.
  • Domyślna polityka to denied i pozostaje taka, dopóki użytkownik nie kliknie.
  • Zapisujemy wybór w cookie i odtwarzamy go przy kolejnych wizytach.
  • GA4 może ładować się dopiero po zgodzie. Jeśli chcesz ładować GA zawsze, usuń loadGA(...) i wstaw standardowy <script src="https://www.googletagmanager.com/gtag/js?..."> w <head>.

Jak dodać do GTM (kolejne kroki)

  1. Włącz zgodność i tryb zgody

    W GTM wejdź: Admin → Container settings → zaznacz Enable consent overview.

  2. Tag 1: Consent defaults (DENIED)

    Utwórz Tag → Custom HTML.

    Tag type: Consent Initialization

    Trigger: Consent Initialization – All Pages

Wklej:

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){ dataLayer.push(arguments); }

  gtag('consent', 'default', {
    ad_storage: 'denied',
    analytics_storage: 'denied',
    functionality_storage: 'denied',
    personalization_storage: 'denied',
    security_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied'
  });

  // prosty setCookie (opcjonalnie)
  function setCookie(n, v, d){
    var e = "";
    if (typeof d === "number") {
      var dt = new Date(); dt.setTime(dt.getTime() + d*864e5);
      e = "; expires=" + dt.toUTCString();
    }
    var s = location.protocol === "https:" ? "; Secure" : "";
    document.cookie = encodeURIComponent(n) + "=" + encodeURIComponent(v) + e + "; path=/; SameSite=Lax" + s;
  }

  // zapisz domyślną decyzję
  setCookie('consent', JSON.stringify({
    ad_storage:'denied', analytics_storage:'denied',
    functionality_storage:'denied', personalization_storage:'denied',
    security_storage:'denied', ad_user_data:'denied', ad_personalization:'denied'
  }), 180);
</script>

1. Tag 2: Consent update (GRANTED po zgodzie)

Utwórz Tag → Custom HTML.

Trigger: Custom Event o nazwie `consent_granted` (stwórz nowy trigger Custom Event: Event name = `consent_granted`).

Kod:

<script>
  function gtag(){ dataLayer.push(arguments); }
  gtag('consent', 'update', {
    ad_storage: 'granted',
    analytics_storage: 'granted',
    functionality_storage: 'granted',
    personalization_storage: 'granted',
    security_storage: 'granted',
    ad_user_data: 'granted',
    ad_personalization: 'granted'
  });

  // spójnie z tagiem default: zapisz wybór
  (function setCookie(n, v, d){
    var e = "";
    if (typeof d === "number") {
      var dt = new Date(); dt.setTime(dt.getTime() + d*864e5);
      e = "; expires=" + dt.toUTCString();
    }
    var s = location.protocol === "https:" ? "; Secure" : "";
    document.cookie = encodeURIComponent(n) + "=" + encodeURIComponent(v) + e + "; path=/; SameSite=Lax" + s;
  })('consent', JSON.stringify({
    ad_storage:'granted', analytics_storage:'granted',
    functionality_storage:'granted', personalization_storage:'granted',
    security_storage:'granted', ad_user_data:'granted', ad_personalization:'granted'
  }), 180);
</script>

1. Powiąż baner z GTM

Masz dwie proste opcje. Wybierz jedną.

A) Z poziomu kodu banera (najprościej): po kliknięciu „Akceptuję” wyślij event do dataLayer:

<script>
  // w Twoim kodzie banera (po kliknięciu Akceptuję):
  dataLayer = window.dataLayer || [];
  dataLayer.push({ event: 'consent_granted' });
</script>

To odpali Tag 2 i ustawi granted.

B) Z poziomu GTM (nasłuch klików): jeśli przycisk ma id consent-accept, zrób Trigger → Click → Just Links lub All Elements z warunkiem Click ID equals consent-accept, a potem w Tagu (Custom HTML) uruchamianym tym triggerem wstaw:

<script>
  dataLayer.push({ event: 'consent_granted' });
</script>

Dzięki temu Tag 2 wstrzyknie po kliknięciu.

  1. GA4 w GTM
  • GA4 Configuration Tag może ładować się od razu, ale dzięki Consent Mode dane będą ograniczone przy denied.
  • Upewnij się w GA4 tagu: Advanced Settings → Consent Settings → Use consent settings to improve tag delivery.
  • Jeśli chcesz ładować GA4 dopiero po zgodzie, ustaw trigger GA4 na Custom Event consent_granted zamiast All Pages. To bardziej restrykcyjne podejście.

Szybkie podsumowanie

  • Tag Consent Initialization na All Pages ustawia default = denied.
  • Po akceptacji baner robi dataLayer.push({event:'consent_granted'}).
  • Tag Consent update, odpalany tym eventem, ustawia granted i zapisuje cookie.
  • GA4 albo startuje od razu i respektuje zgodę, albo odpalasz go dopiero po consent_granted (bardziej „privacy-first”).