W Google Analytics 4 w zdefiniowanych raportach możemy zauważyć spojrzenie na dane z perspektywy trzech rodzajów atrybucji:
Atrybucja oparta na danych (Data-Driven) - zamiast sztywnych reguł, wykorzystuje uczenie maszynowe do analizy zarówno ścieżek zakończonych konwersją, jak i tych, które do niej nie doprowadziły.
Atrybucja danych w Google Analytics 4 - uszczelnij śledzenie na swojej usłudze
- Jak to działa: Algorytm ocenia, jak duży wpływ na finalną decyzję miało każde kliknięcie na ścieżce. Jeśli użytkownik kliknął w reklamę na YouTube, potem w newsletter, a na końcu wszedł z wyszukiwarki – DDA przydzieli ułamkową wartość konwersji każdemu z tych punktów (np. 0.2 dla YT, 0.3 dla maila, 0.5 dla organic).
- Problem: Dla analityka jest to "czarna skrzynka". Nie widzimy dokładnie, dlaczego Google przydzielił 20% zasług reklamie displayowej. W codziennej pracy operacyjnej (np. rozliczaniu afiliacji) potrzebujemy systemu zero-jedynkowego: "Kto dostarczył klienta w momencie zakupu?", a nie "Kto mu się przypomniał tydzień temu".
Model Ostatniego Kliknięcia (Last Click) - to podejście jest najbliższe temu, co pamiętamy z Universal Analytics i co najczęściej widzimy w raporcie Pozyskiwanie ruchu (Traffic acquisition). Kluczową zasadą jest tu mechanizm "Last Non-Direct Click" (Ostatnie kliknięcie niebezpośrednie).
- Jak to działa: Jeśli użytkownik wejdzie na stronę z kampanii Google Ads (cpc), a następnie wróci na nią, wpisując adres bezpośrednio w przeglądarce (direct), GA4 zignoruje wejście direct i przypisze sesję do ostatniego znanego źródła (Google Ads).
- Problem: Choć brzmi to logicznie, mechanizm ten w GA4 bywa zawodny w przypadku specyficznych przekierowań np. powrót podczas otwartej sesji z nowego linku z UTM. W Universal takie zachowanie powodowało nową sesje i nowe źródło. W większości przypadkach starszy brat Universala rozwiązał to w lepszy sposób - są jednak specyficzne sytuacje w których właśnie zależy nam na tym, aby takie źródło zostało przekazane.
Pierwsze źrodło użytkownika (First User Source) - ten wymiar patrzy w przeszłość – tak daleko, jak pozwala na to czas życia ciasteczka (lub identyfikator User-ID). Odpowiada na pytanie: "Jak ten człowiek dowiedział się o nas po raz pierwszy?".
- Jak to działa: Nieważne, że użytkownik wszedł właśnie z newslettera i kupił. Jeśli 3 miesiące temu pierwszy raz trafił na stronę z reklamy na Facebooku, w raporcie Pozyskiwanie użytkowników (User acquisition) konwersja zostanie przypisana do Facebooka.
- Problem: Ten model jest świetny dla dyrektorów marketingu oceniających strategię wzrostu (Growth), ale fatalny dla e-commerce managerów, którzy muszą wiedzieć, która kampania "domknęła" koszyk dzisiaj rano. Ignoruje on całą drogę perswazji, która nastąpiła po pierwszym kontakcie.
Dlaczego może przydać nam się nowy sposób mierzenia danych?
1. Dosyłka informacji o brakujących ścieżkach kanału np. w przypadku działań afiliacyjnych. Kluczową zaletą tego rozwiązania jest inteligentne zarządzanie momentem opuszczenia witryny przez użytkownika, na przykład w celu poszukiwania kodu rabatowego. Podczas gdy GA4 w takich scenariuszach najprawdopodobniej nie nadpisze nowego źródła (np. Google Ads), wykorzystany skrypt wykorzystuje mechanizm przywracania danych z ciasteczka. W przypadku powrotu z linku afiliacyjnego będziemy w stanie przypisać ten zakup do tego źródła. Słusznie GA4 z automatu nie powoduje nadpisania tego źródła, ponieważ byłoby to niesprawiedliwie, że jakiekolwiek działania na stronie nadpisują faktyczne sprowadzenie użytkownika na portal. Możemy jednak mieć potrzebę zbierania takich informacji i wtedy skrypt sprawdzi się idealnie.
2. Weryfikacja i porównanie danych z GA4. Nie musimy ślepo wierzyć w dane dostarczane przez narzędzie. W sytuacjach, gdy Google Analytics 4 modeluje konwersje (np. przy braku pełnych zgód cookie) lub w niejasny sposób przypisuje zasługi poszczególnym kanałom, własny skrypt dostarcza niezbędnego punktu odniesienia. Działa on w oparciu o logikę zero-jedynkową, uznając obecność parametru UTM w adresie URL za bezsporny dowód źródła wizyty. Dzięki temu analityk dysponuje zestawem surowych danych porównawczych , które pozwalają zweryfikować, czy algorytmy Google w danym przypadku nie zniekształcają rzeczywistego obrazu efektywności kampanii.
3. Integracja z systemami CRM. Dzięki zapisywaniu źródła ruchu bezpośrednio w sessionStorage przeglądarki, otwieramy możliwość przesyłania danych analitycznych poza ekosystem Google, wprost do systemów sprzedażowych. Programiści mogą z łatwością pobrać te informacje i umieścić je w ukrytych polach formularzy kontaktowych, co sprawia, że handlowiec w systemie CRM (takim jak HubSpot czy Salesforce) natychmiast widzi precyzyjne źródło pozyskania leada (np. konkretną kampanię LinkedIn), bez konieczności oczekiwania na przetworzenie danych przez opóźnione raporty analityczne.
Jak działa skrypt wykorzystany do przesyłania informacji o źródłe danych?
Skrypt jest narzędziem nasłuchującym, które uruchamia się przy każdym załadowaniu strony. Autorem skryptujest firma Lunametrics (obecnie część firmy Bounteous) oraz został udoskonalony przez Juliusa Fedoroviciusa z Analytics Mania. Skrypt edytowałem, aby dodatkowo przekazywał 3 parametry traffic_source, traffic_medium oraz traffic_campaign to session_storage.
<script>
/**
* Traffic Source Tracker for bettersteps.pl
* Based on Lunametrics & Analytics Mania logic
* Features: Cookie storage (First Click model) + sessionStorage support
*/
(function(document) {
var cookieName = 'initialTrafficSource';
var sessionCookieName = '__utmzzses';
var cookieExpiration = new Date(+new Date() + 1000 * 60 * 60 * 24 * 30 * 24);
var referrer = document.referrer;
var gaReferral = {
'utmcsr': '(direct)',
'utmcmd': '(none)',
'utmccn': '(not set)'
};
var thisHostname = document.location.hostname;
var thisDomain = getDomain_(thisHostname);
var referringDomain = getDomain_(document.referrer);
var sessionCookie = getCookie_(sessionCookieName);
var qs = document.location.search.replace('?', '');
var hash = document.location.hash.replace('#', '');
var gaParams = parseGoogleParams(qs + '#' + hash);
var referringInfo = parseGaReferrer(referrer);
var storedVals = getCookie_(cookieName) || getCookie_('__utmz') || getCookie_('__utmzz');
var newCookieVals = [];
var keyMap = {
'utm_source': 'utmcsr',
'utm_medium': 'utmcmd',
'utm_campaign': 'utmccn',
'utm_content': 'utmcct',
'utm_term': 'utmctr',
'gclid': 'utmgclid',
'dclid': 'utmdclid'
};
var keyFilter = ['utmcsr', 'utmcmd', 'utmccn', 'utmcct', 'utmctr'];
var key, i, len, _val, _key, raw, keyName, values;
if (sessionCookie && referringDomain === thisDomain) {
gaParams = null;
referringInfo = null;
}
if (gaParams && (gaParams.utm_source || gaParams.gclid || gaParams.dclid)) {
for (key in gaParams) {
if (typeof gaParams[key] !== 'undefined') {
keyName = keyMap[key];
gaReferral[keyName] = gaParams[key];
}
}
if (gaParams.gclid || gaParams.dclid) {
gaReferral.utmcsr = 'google';
gaReferral.utmcmd = gaReferral.utmgclid ? 'cpc' : 'cpm';
}
}
else if (referringInfo) {
gaReferral.utmcsr = referringInfo.source;
gaReferral.utmcmd = referringInfo.medium;
if (referringInfo.term) gaReferral.utmctr = referringInfo.term;
}
else if (storedVals) {
values = {};
raw = storedVals.split('|');
len = raw.length;
for (i = 0; i < len; i++) {
_val = raw[i].split('=');
_key = _val[0].split('.').pop();
values[_key] = _val[1];
}
gaReferral = values;
}
for (key in gaReferral) {
if (typeof gaReferral[key] !== 'undefined' && keyFilter.indexOf(key) > -1) {
newCookieVals.push(key + '=' + gaReferral[key]);
}
}
writeCookie_(cookieName, newCookieVals.join('|'), cookieExpiration, '/', thisDomain);
writeCookie_(sessionCookieName, 1, null, '/', thisDomain);
try {
var s_source = gaReferral.utmcsr || '(direct)';
var s_medium = gaReferral.utmcmd || '(none)';
var s_campaign = gaReferral.utmccn || '(not set)';
sessionStorage.setItem('traffic_source', s_source);
sessionStorage.setItem('traffic_medium', s_medium);
sessionStorage.setItem('traffic_campaign', s_campaign);
} catch (e) {
}
function parseGoogleParams(str) {
var campaignParams = ['source', 'medium', 'campaign', 'term', 'content'];
var regex = new RegExp('(utm_(' + campaignParams.join('|') + ')|(d|g)clid)=.*?([^&#]*|$)', 'gi');
var gaParams = str.match(regex);
var paramsObj, vals, len, i;
if (gaParams) {
paramsObj = {};
len = gaParams.length;
for (i = 0; i < len; i++) {
vals = gaParams[i].split('=');
if (vals) {
paramsObj[vals[0]] = vals[1];
}
}
}
return paramsObj;
}
function parseGaReferrer(referrer) {
if (!referrer) return;
var searchEngines = {
'daum.net': {'p': 'q','n': 'daum'},
'eniro.se': {'p': 'search_word','n': 'eniro '},
'naver.com': {'p': 'query','n': 'naver '},
'yahoo.com': {'p': 'p','n': 'yahoo'},
'msn.com': {'p': 'q','n': 'msn'},
'bing.com': {'p': 'q','n': 'live'},
'aol.com': {'p': 'q','n': 'aol'},
'lycos.com': {'p': 'q','n': 'lycos'},
'ask.com': {'p': 'q','n': 'ask'},
'altavista.com': {'p': 'q','n': 'altavista'},
'search.netscape.com': {'p': 'query','n': 'netscape'},
'cnn.com': {'p': 'query','n': 'cnn'},
'about.com': {'p': 'terms','n': 'about'},
'mamma.com': {'p': 'query','n': 'mama'},
'alltheweb.com': {'p': 'q','n': 'alltheweb'},
'voila.fr': {'p': 'rdata','n': 'voila'},
'search.virgilio.it': {'p': 'qs','n': 'virgilio'},
'baidu.com': {'p': 'wd','n': 'baidu'},
'alice.com': {'p': 'qs','n': 'alice'},
'yandex.com': {'p': 'text','n': 'yandex'},
'najdi.org.mk': {'p': 'q','n': 'najdi'},
'seznam.cz': {'p': 'q','n': 'seznam'},
'search.com': {'p': 'q','n': 'search'},
'wp.pl': {'p': 'szukaj ','n': 'wirtulana polska'},
'online.onetcenter.org': {'p': 'qt','n': 'o*net'},
'szukacz.pl': {'p': 'q','n': 'szukacz'},
'yam.com': {'p': 'k','n': 'yam'},
'pchome.com': {'p': 'q','n': 'pchome'},
'kvasir.no': {'p': 'q','n': 'kvasir'},
'sesam.no': {'p': 'q','n': 'sesam'},
'ozu.es': {'p': 'q','n': 'ozu '},
'terra.com': {'p': 'query','n': 'terra'},
'mynet.com': {'p': 'q','n': 'mynet'},
'ekolay.net': {'p': 'q','n': 'ekolay'},
'rambler.ru': {'p': 'words','n': 'rambler'},
'google': {'p': 'q','n': 'google'}
};
var a = document.createElement('a');
var values = {};
var searchEngine, termRegex, term;
a.href = referrer;
if (a.hostname.indexOf('google') > -1) {
referringDomain = 'google';
}
if (searchEngines[referringDomain]) {
searchEngine = searchEngines[referringDomain];
termRegex = new RegExp(searchEngine.p + '=.*?([^&#]*|$)', 'gi');
term = a.search.match(termRegex);
values.source = searchEngine.n;
values.medium = 'organic';
values.term = (term ? term[0].split('=')[1] : '') || '(not provided)';
} else if (referringDomain !== thisDomain) {
values.source = a.hostname;
values.medium = 'referral';
}
return values;
}
function writeCookie_(name, value, expiration, path, domain) {
var str = name + '=' + value + ';';
if (expiration) str += 'Expires=' + expiration.toGMTString() + ';';
if (path) str += 'Path=' + path + ';';
if (domain) str += 'Domain=' + domain + ';';
document.cookie = str;
}
function getCookie_(name) {
var cookies = '; ' + document.cookie;
var cvals = cookies.split('; ' + name + '=');
if (cvals.length > 1) return cvals.pop().split(';')[0];
}
function getDomain_(url) {
if (!url) return;
var a = document.createElement('a');
a.href = url;
try {
return a.hostname.match(/[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?$/)[0];
} catch (squelch) {
return a.hostname;
}
}
})(document);
</script>
Najważniejszą funkcjonalnościa skryptu jest tworzenie ciasteczka o nazwie “initialTrafficSource”, który przekazuje informacje o źródle danych. Te informacje jesteśmy w stanie wykorzystać i przesłać do różnych narzędzi. My oczywiście w tym artykule skupiamy się na wysyłce ich do Google Analytics.

Podczas przekazywania źródła, logika jest następująca:
Krok A: Filtracja Ruchu Wewnętrznego Skrypt sprawdza: Czy użytkownik ma aktywne ciasteczko sesyjne i czy przyszedł z naszej własnej domeny? Jeśli TAK – skrypt "zasypia". Nie robi nic, aby nie nadpisać np. wejścia z Google Ads ruchem wewnętrznym podczas przechodzenia między podstronami.
Krok B: Parametry URL
Skrypt sprawdza adres URL.
- Jeśli znajdzie utm_source, gclid (Google Ads) lub dclid – bezwzględnie nadpisuje dotychczasowe źródło nowymi danymi.
- To gwarantuje, że jeśli ktoś kliknie w link afiliacyjny lub nową reklamę, zostanie to odnotowane natychmiast.
Krok C: Referrer (Odesłanie) Jeśli w URL nie ma parametrów, skrypt patrzy na document.referrer (stronę, z której przyszedł użytkownik).
- Skrypt posiada wbudowaną bazę wyszukiwarek (funkcja parseGaReferrer). Jeśli rozpoznaje google.com, bing.com itp., przypisuje źródło organic.
- Jeśli to inna strona (np. Facebook, blog), przypisuje referral.
Krok D: Przywracanie z Pamięci (Cookie Restore) Jeśli nie ma UTM-ów ani zewnętrznego referrera (czyli wejście jest Direct lub odświeżenie strony), skrypt sprawdza ciasteczko initialTrafficSource.
- Jeśli znajdzie tam zapisane wcześniej dane, przywraca je. Dzięki temu wejście bezpośrednie nie "nadpisuje" wcześniejszej kampanii marketingowej pustymi danymi (direct/none).
Na samym końcu, po ustaleniu "zwycięskiego" źródła, skrypt zapisuje je 2 cookies oraz w sessionStorage: ciasteczko główne (initialTrafficSource) oraz sessionStorage (zapisuje traffic_source, traffic_medium, traffic_campaign w pamięci podręcznej przeglądarki).

Wdrożenie przekazywanych danych do Google Analytics 4
W celu przesłania tych danych do GA4 możesz wykorzystać przygotowane wartości w sessionStorage. Na przykładzie zmiennej traffic_source, stwórz zmienną typu Niestandardowy kodJavaScript z funkcją:
function() {
return sessionStorage.getItem('traffic_source');
}

Analogiczne zrób traffic_medium oraz traffic_campaign. Kolejnym krokiem, który wykonałem, było wykorzystanie ustawień zdarzeń, które wykorzystuje do przekazywania stałych parametrów do wszystkich zdarzeń wysyłanych do Google Analytics 4.

Po edycji ustawień zdarzeń miałem je już wszystkie automatycznie stworzone we wszystkich tagach związanych z Google Analytics 4. Jeśli nie wykorzystujesz ustawień zdarzeń, możesz dodać ręcznie parametry traffic_campaign, traffic_medium oraz traffic_source do zdarzenia w którym chcesz mieć je przekazywane np. purchase lub page_view.

Ostatnim krokiem wdrożeniowym jest deklaracja przekazywanych przez GTM zmiennych (traffic_source, traffic_medium i traffic_campaign) w Google Analytics 4, aby były one odbierane w samym narzędziu.

Analiza danych - dane GA4 vs dane z wykorzystanego skryptu
Po wdrożeniu rozwiązania rekomenduję przygotowanie raportu w eksploracji, gdzie możemy przeanalizować interesujące nas zdarzenie (pamiętajmy o wysyłaniu do tego zdarzenia naszych dodatkowych zmiennych) przez pryzmat źródła danych w atrybucji Google oraz skryptu opisanego w artykule. Jeśli mamy długą historię danych w GA4 pamiętajmy o tym, że Google Analytics pamięta o ostatniej sesji non-direct. Wyobraźmy sobie, że miesiąc wcześniej wszedłem na stronę przez Google Ads. Wczoraj pojawiłem się już tam wpisując adres www bezpośrednio w przeglądarce. Oznacza to, że w GA4 źródłem będzie Google Ads, a wg naszego skryptu direct/not set.

Podsumowanie
W świecie, w którym GA4 coraz więcej "modeluje", posiadanie niezależnego systemu weryfikacji jest polisą ubezpieczeniową dla Twoich danych oraz może wzbogacić ścieżkę klienta w niektórych przypadkach jak np. specyficzne działania afiliacyjne (rabaty z linków z UTM przy już otwartej sesji).
Pamiętaj, że to rozwiązanie nie ma zastąpić GA4, ale je uzupełnić. Daje Ci ono drugą parę oczu, która patrzy na ruch dokładnie w taki sposób, w jaki zdefiniowałeś to w kodzie.Zaimplementuj skrypt w środowisku testowym GTM, włącz podgląd (Preview Mode) i przeprowadź test. Kliknij w swój link z UTM, dodaj produkt, wejdź z innego UTM-a i sprawdź w konsoli przeglądarki wg umieszczonych screenów w artykule, czy wartości traffic_source podmieniły się natychmiastowo. Jeśli tak – jesteś gotowy na wdrożenie produkcyjne.