Odnawianie ciasteczek First-Party: Ochrona przed ITP dzięki Cloudflare Workers i ssGTM

Spis treści
- Odnawianie ciasteczek First-Party: Ochrona przed ITP dzięki Cloudflare Workers i ssGTM
- 1. Projekt architektury: Ciasteczka zarządzane przez serwer
- 2. Odnawianie cookie przez Cloudflare Workers i ssGTM
- 3. Google Tag Gateway (GTG): Idealne partnerstwo na krawędzi
- Podsumowanie
- Wariant: przedłużanie własnego ciasteczka _ga tagu Google
- Pytania i odpowiedzi
- Źródła
Odnawianie ciasteczek First-Party: Ochrona przed ITP dzięki Cloudflare Workers i ssGTM
Mechanizm Intelligent Tracking Prevention (ITP) w przeglądarce Safari oraz nowoczesne silniki prywatności drastycznie ograniczają żywotność ciasteczek po stronie klienta. Identyfikatory ustawiane za pomocą JavaScriptu (document.cookie) są ograniczane do maksymalnie 7 dni – a w przypadku wykrycia parametrów kliknięć reklamowych w adresie URL ich ważność jest redukowana nawet do 24 godzin. Również klasyczne wdrożenia oparte na maskowaniu CNAME są identyfikowane i neutralizowane przez algorytmy obronne WebKit, gdy rekord DNS wskazuje na zewnętrzne serwerownie systemów śledzących.
Aby zachować spójność danych oraz precyzję atrybucji w długich cyklach konwersji, architektura analityczna musi zrezygnować z pamięci zarządzanej przez skrypty na rzecz prawdziwego trasowania First-Party na krawędzi sieci (Edge Routing) przy użyciu nagłówków HTTP zarządzanych przez serwer.
1. Projekt architektury: Ciasteczka zarządzane przez serwer
Ominięcie 7-dniowego limitu ITP wymaga generowania identyfikatorów bezpośrednio w warstwie brzegowej za pomocą nagłówków odpowiedzi HTTP (Set-Cookie). Ponieważ restrykcje przeglądarek odróżniają ciasteczka tworzone przez skrypty klienta od legalnych sesji generowanych przez serwer, ciasteczka skonfigurowane przez Edge Workers mogą zachować ważność nawet przez 365 dni, przy pełnej zgodności z regionalnymi regulacjami o prywatności i pod warunkiem, że odpowiedź pochodzi z własnej infrastruktury witryny: jeśli host odpowiadający jest osiągany przez CNAME lub adres IP zewnętrznego dostawcy, WebKit ogranicza również ciasteczka ustawione w odpowiedzi HTTP do 7 dni.
| Atrybut ciasteczka | Wymagana wartość | Uzasadnienie techniczne |
|---|---|---|
HttpOnly | true | Blokuje dostęp z poziomu JavaScriptu, uodparniając ciasteczko na limity ITP oraz ataki XSS. |
Secure | true | Wymaga szyfrowanej transmisji HTTPS w całych strukturach sieciowych. |
SameSite | Lax / Strict | Gwarantuje traktowanie ciasteczka jako natywnego kontekstu First-Party. |
Domain | Domena główna (np. example.com) | Zapobiega karom za niezgodność CNAME pomiędzy subdomenami. |
2. Odnawianie cookie przez Cloudflare Workers i ssGTM
Zaawansowany potok odnawiania identyfikatorów wykorzystuje Cloudflare Workers jako reverse proxy przed kontenerem Server-Side Google Tag Manager (ssGTM). W momencie przechwycenia żądania HTTP na krawędzi sieci, skrypt Worker weryfikuje obecność prawidłowego identyfikatora serwerowego. W przypadku jego braku Worker wstrzykuje do odpowiedzi nowy nagłówek Set-Cookie.
// Cloudflare Worker Edge Script: Server-Managed Identifier Restoration
function getCookieValue(cookieHeader, name) {
if (!cookieHeader) return null;
for (const part of cookieHeader.split("; ")) {
const eq = part.indexOf("=");
if (eq !== -1 && part.slice(0, eq) === name) return part.slice(eq + 1);
}
return null;
}
export default {
async fetch(request, env) {
const response = await fetch(request);
const newResponse = new Response(response.body, response);
// Odczyt lub wygenerowanie anonimowego identyfikatora First-Party
let fpId = getCookieValue(request.headers.get("Cookie"), "fp_id");
if (!fpId) {
fpId = crypto.randomUUID();
// Nagłówek Set-Cookie z 1-rocznym okresem ważności tylko przy braku identyfikatora
newResponse.headers.append(
"Set-Cookie",
`fp_id=${fpId}; Max-Age=31536000; Path=/; Domain=example.com; Secure; HttpOnly; SameSite=Lax`
);
}
return newResponse;
}
};
Wewnątrz środowiska ssGTM ten trwały identyfikator brzegowy jest mapowany na standardowe pole Client ID dla GA4 lub Google Ads, co stabilizuje wielokanałową atrybucję w długoterminowych ścieżkach użytkowników.
3. Google Tag Gateway (GTG): Idealne partnerstwo na krawędzi
Google Tag Gateway (GTG) to wyspecjalizowany mechanizm trasowania First-Party, który tuneluje ruch analityczny bezpośrednio przez główną domenę. Wdrożenie GTG na platformie Cloudflare Workers tworzy idealną synergię z procesem odnawiania ciasteczek:
- Kontekst Same-Origin: GTG przesyła biblioteki skryptów (np.
/gtg/gtm.js) oraz pakiety danych (/gtg/collect) przez dokładnie ten sam adres IP oraz numer systemu autonomicznego (ASN), co aplikacja główna. - Odporność na mechanizmy obronne CNAME: Ponieważ GTG działa bezpośrednio w infrastrukturze domeny głównej poprzez proxy brzegowe zamiast zewnętrznych delegacji DNS, algorytmy antyśledzące WebKit klasyfikują ten ruch jako natywną komunikację backendową.
- Brak osobnego połączenia śledzącego: Ponieważ trasowanie GTG oraz odnawianie ciasteczek w tym samym procesie Cloudflare Worker odbywa się pod nazwą hosta witryny, przeglądarka może ponownie użyć istniejącego połączenia zamiast nawiązywać uścisk dłoni TLS z osobną domeną śledzącą; przekazanie żądania przez Worker do kontenera serwerowego nadal stanowi dodatkowy krok sieciowy.
Podsumowanie
Przezwyciężenie ograniczeń ITP wymaga przeniesienia zarządzania identyfikatorami ze skryptów przeglądarki do infrastruktury brzegowej. Połączenie serwerowych ciasteczek HttpOnly w Cloudflare Workers z trasowaniem Same-Origin oferowanym przez Google Tag Gateway (GTG) zapewnia w pełni zgodną z prawem, bezpieczną i technicznie niezawodną architekturę pomiarową.
Wariant: przedłużanie własnego ciasteczka _ga tagu Google
Opisany wyżej Worker tworzy osobny identyfikator fp_id, mapowany w kontenerze serwerowym na Client ID. Węższy wariant pozostawia identyfikator tagowi Google i jedynie odnawia istniejące ciasteczko _ga w odpowiedzi HTTP, na przykład z osobnego punktu końcowego pod tą samą nazwą hosta. O tym, czy to działa, decydują cztery szczegóły.
- Bez
HttpOnlydla_ga: tag Google odczytuje Client ID przez JavaScript, a ciasteczkoHttpOnlynie jest dostępne przezdocument.cookie. Tabela atrybutów powyżej dotyczy osobnego identyfikatora serwerowego, a nie_ga. - Zgodny atrybut
Domain: nowe ciasteczko zastępuje istniejące tylko wtedy, gdy nazwa, domena i ścieżka są identyczne. Przycookie_domain: 'auto'tag Google zapisuje ciasteczka w najwyższej możliwej domenie, czyli dla www.example.com w example.com. Worker zapisujący_gazDomain=www.example.comtworzy drugie ciasteczko zamiast przedłużyć pierwsze. cookie_update: falsew tagu Google: przy domyślnej wartościtruetag może aktualizować ciasteczka przy każdym wczytaniu strony i liczyć ważność od ostatniej wizyty, co w Safari oznacza znów ciasteczko zapisane skryptem. Przyfalseciasteczka nie są aktualizowane przy każdym wczytaniu strony.- Odnawiać, nigdy nie tworzyć: jeśli żądanie nie zawiera
_ga, punkt końcowy odpowiada bezSet-Cookie. W przeciwnym razie przy pierwszej wizycie zarówno Worker, jak i tag nadają własny identyfikator, a jeden odwiedzający może zostać policzony jako dwóch użytkowników.
Google podaje dla _ga domyślny okres ważności dwóch lat, co odpowiada Max-Age=63072000. Od wersji 104 (sierpień 2022) Chrome ogranicza ważność ciasteczek do 400 dni: dłuższe wartości nie są odrzucane, lecz skracane do 400 dni, a każde odnowienie przy późniejszej wizycie liczy ten okres od nowa.
Ustawienie ciasteczka nagłówkiem odpowiedzi nie zmienia obowiązku uzyskania zgody. Art. 5 ust. 3 dyrektywy o prywatności i łączności elektronicznej obejmuje przechowywanie informacji w urządzeniu i dostęp do nich niezależnie od techniki, dlatego wywołanie punktu końcowego odnawiającego ciasteczko następuje dopiero po udzieleniu zgody analytics_storage.
Pytania i odpowiedzi
Czy przy buforowaniu stron na brzegu sieci Worker powinien zapisywać ciasteczko przy każdej odpowiedzi, czy tylko tam, gdzie brakuje identyfikatora?
Tylko tam, gdzie go brakuje albo gdzie zbliża się koniec jego ważności. Fragment kodu w artykule pokazuje mechanizm, a nie umiejscowienie, a to właśnie umiejscowienie decyduje, czy strona pozostaje buforowalna.
Odpowiedź z nagłówkiem Set-Cookie nie powinna trafiać do współdzielonego bufora, a wiele współdzielonych buforów domyślnie odmawia jej zapisania. To reguła poprawności, a nie opcja strojenia, bo kopia w buforze przekazałaby identyfikator jednego odwiedzającego następnemu. Zależnie od tego, w którym miejscu łańcucha nagłówek zostaje dodany, dołączanie go do każdej odpowiedzi HTML może więc wyłączyć buforowanie na brzegu dla całej strony, a ten koszt przewyższa wszystko, co zyskuje pomiar.
Dwie zmiany pozwalają zachować jedno i drugie. Nagłówek jest zapisywany tylko na ścieżce, która i tak musi być indywidualna – w punkcie końcowym analityki wywoływanym przez tag, a nie na stronie – i tylko wtedy, gdy ciasteczka brakuje albo jest ono w oknie odnowienia. To, co dociera do kontenera, pozostaje bez zmian, bo identyfikator i tak podróżuje razem z żądaniem. Nagłówek statusu bufora w odpowiedzi strony pokazuje bieżący stan: jeśli przy każdym żądaniu widnieje tam miss, pierwszym podejrzanym jest ciasteczko zapisywane bezwarunkowo.
Jak usunąć ciasteczko HttpOnly, takie jak fp_id z rocznym okresem ważności, gdy zgoda zostanie wycofana?
Tą samą drogą, którą powstało: to, co ustawia serwer, serwer musi też wycofać. To obowiązek, który ta architektura ze sobą niesie, a którego brakuje w wielu opisach.
W praktyce jest to druga ścieżka w tym samym Workerze, która nadpisuje ciasteczko wartością Max-Age=0, wywoływana ze ścieżki wycofania zgody w narzędziu do zarządzania zgodami. Bez tej ścieżki identyfikator przetrwa rok, choć jego podstawa przestała istnieć, i nie ma też żadnego dowodu, że został usunięty. Równie ważna jest ścieżka ustawiania: Worker z artykułu zapisuje ciasteczko, gdy tylko go brakuje. Jeśli nie sprawdza tam zgody, już następne żądanie po usunięciu tworzy nowy identyfikator.
Jeden szczegół rozstrzyga o skutku albo jego pozorach: ciasteczko usuwające musi mieć tę samą nazwę, tę samą domenę i tę samą ścieżkę co pierwotne, w przykładzie więc Domain=example.com i Path=/. Jeśli domena lub ścieżka się różnią, polecenie trafia w próżnię, a stare ciasteczko zostaje. Odpowiedź Workera mimo to wygląda poprawnie, bo nagłówek Set-Cookie jest obecny; dopiero wgląd w faktycznie zapisane ciasteczka pokazuje, że fp_id wciąż istnieje.
Czy ciasteczko na domenie głównej, wysyłane z każdym żądaniem, także po zasoby statyczne, nie psuje buforowania na CDN?
Psuje, jeżeli klucz bufora uwzględnia ciasteczka, a wiele konfiguracji domyślnie tak robi, żeby nie wydać treści zalogowanego użytkownika komuś innemu.
Efekt jest cichy i kosztowny: każdy odwiedzający ma inną wartość identyfikatora, więc każdy dostaje własną kopię w buforze. Współczynnik trafień spada niemal do zera, obciążenie serwera źródłowego rośnie, a strona staje się wolniejsza bez jednego komunikatu o błędzie.
Rozwiązanie polega na wyłączeniu tego konkretnego ciasteczka z klucza bufora; u większości dostawców to jedno pole w konfiguracji. Warto zrobić to świadomie i zapisać, bo reguła wygląda niebezpiecznie („ignoruj ciasteczko”), a w rzeczywistości jest bezpieczna: identyfikator analityczny nie zmienia treści strony, więc nie ma powodu, żeby dzielił bufor na tyle części, ilu jest odwiedzających.
Źródła
- Full Third-Party Cookie Blocking (WebKit)
- Tracking Prevention in WebKit: CNAME and third-party IP address cloaking defense
- Cloudflare Workers documentation
- Google Analytics: Wykorzystanie ciasteczek w witrynach
- Google tag: Customize cookies
- Chrome for Developers: Cookie Expires and Max-Age attributes now have upper limit
- MDN: Set-Cookie
- RFC 6265: HTTP State Management Mechanism, Section 5.3
- Dyrektywa 2002/58/WE (dyrektywa o prywatności i łączności elektronicznej)