Чистий серверний трекінг: екскурсія по сетапу

Опубліковано:

Я обійшовся без web GTM, переніс збирання пейлоуду в Cloudflare Worker і зробив GA4 споживачем своїх даних, а не їх джерелом.

Якби ж то мені сказали 7 років тому, в яких хащах трекінгу я буду вовтузитися, я б собі не повірив. От знав я ще з початку своєї карʼєри - хочеш, щоб Universal Analytics збирала дані на сайті? Встанови скрипт. Скрипт… Тоді в мене в голові ще не існувало категорій client-side / server-side, а принцип роботи аналітики був чимось дійсно магічним. Та тепер я тут, на крайньому (на даний момент) етапі еволюції трекінгу, і буду розказувати вам про нюанси і підходи, для котрих поставити скрипт на сайт буде явно недостатньо.

Хоч тему статті я вже визначив, оскільки це перша стаття на блозі, мені не відпетляти від короткого інтро про себе. Я працюю технічним маркетологом вже понад сім років і на власні очі спостерігав етапи розвитку веб-аналітики і трекінгу: перші спроби працювати з сирими даними в Analytics 360 (для особливо багатих), перші дзвіночки про серверний трекінг, повальне захоплення аналізом сирих даних GA4 в силу доступності експорту до BigQuery… На той час все, що відмінне від клієнтського трекінгу, виглядало для мене дійсно, як рокетсаєнс. Зараз, у 2026 році, серверний трекінг вже не виглядає якоюсь непосильною задачою. Великий і щирий шаут-аут за це команді Стейпу, як головним євангелістам сервер-сайд трекінгу.

Так ось. На етапі першого релізу Гуглами серверного ГТМу я думав, що це – безпосередньо заміна звичайному веб-контейнеру. Ба більше, я певен, що багато маркетологів досі так і вважають. Інтуітивно відчувається, що серверний трекінг – це обмін даними між сервером нашого сайту і вендорами, чи то Гугл аналітика або Google Ads. Але реальність трохи складніша і, щоб дані зʼявилися у нас на сервері, вони мають «відбутися», «народитися» в браузері користувача . Лише потім вони опиняються у нас на сервері.

Нам продають покращення швидкості завантаження сторінок при використанні сервер-сайд ГТМу шляхом мінімізації кількості трекінгового js-коду в браузері користувача. Однак, делікатно не уточнюють, що на 100% позбутися браузерного трекінгу навряд вдасться – нам треба щось, що буде збирати всяку телеметрію користувача. Без цього, деяких даних в вашій аналітиці просто не зʼявиться.

Дві схеми поруч. Гібридний сервер-сайд: веб-контейнер GTM і gtag.js збирають пейлоуд у браузері, звідти він іде в контейнер sGTM, далі в GA4, BQ і Data Studio. Чистий сервер-сайд: на сторінці немає трекінгового скрипта, пейлоуд збирає Cloudflare, він же слугує сховищем і живить BI-тул напряму, а контейнер sGTM і GA4 – опційні. Тонкий клієнт на 1,5 КБ шле крихти, чого не бачить сервер.

І мені останні пару років моєї щільної роботи з сервер-сайдом, аж свербіло в голові, як хотілося не зупинятися на чомусь «гібридному» серверному відстеженні і протоптати стежинку далі, випередивши самих Гуглів, Майкрософтів або, на крайняк, Stape. І я почав брейнштормити «А що, якщо?»

А що там в Смарагдовому місті?

Смарагдове місто за моїм сетінгом – то мій блог, якщо що. Так ось, цей сайт – моя лабораторія, місце, де я можу дозволити собі будь-які експерименти. Місце, де я абсолютно безпечно можу шукати способи втілення усіх моїх найбезумніших фантазій. Які фантазії, ви спитаєте?

  • Наприклад, збір даних і формування пейлоуду не в браузері користувача, а на сервері;
  • Альтернативний стрімінг сирих даних замість нативного експорту GA4 в BigQuery;
  • Логічний наступний крок – альтернативний постпроцесінг даних (підходи до атрибуції, до прикладу);
  • Відмінні від стандартних експортів BigQuery підходи на дата-інженерному поприщі;
  • Свої спроби боротьби з ботами;
  • GA4, як користувач, а не власник даних;
  • Грамотна візуалізація контент-проекту, котрим я бачу цей блог в майбутньому (відкладемо на момент, коли буде хоч якийсь трафік);

Швиденько пробіжимося по всіх пунктах, але не сумнівайтесь, що більш детально про кожен з них я буду писати в майбутньому, як окремі статті.

Етап 1: формування хітів і їх відправка в sGTM на базі Stape

Скажу максимально приземлено і в контексті веб-трекінгу, що хіт – це мінімальна одиниця, зерно, grain, кожна окрема подія-івент, що виникає під час взаємодії окремого користувача з сайтом. При суто клієнтському трекінгу або «серверному» (в лапках, бо говорю про гібридний), ці хіти збираються в браузері за допомогою gtagʼу і звідти відправляються на сторону вендора (або опціонально на серверний контейнер таг менеджеру).

В моєму рішенні пейлоуд збирається воркером на стороні Cloudflare, він же відповідає за ідентифікацію користувачів та їх сесій (грубо кажучи, проставляє куки з відповідними айдішками від імені серверу). Цей пейлоуд виходить майже 1в1 з тим, що відправляє ГА4 з браузера, таким чином, ідеально сприймається нативним клієнтом GA4 на стороні sGTM’у.

Сирий івент, яким його збирає воркер: JSON з полями ts, at, url, event і method нагорі, двома згорнутими блоками observed та did і об’єктом payload, у якому лежать поля GA4 — tid, cid, en, dl, dr, dt, sid, ul, підміна IP, регіон, часовий пояс, мова сайту, collected_by, event_key і поля згоди gcs та gcd.Сирий івент, яким його збирає воркер: JSON з полями ts, at, url, event і method нагорі, двома згорнутими блоками observed та did і об’єктом payload, у якому лежать поля GA4 — tid, cid, en, dl, dr, dt, sid, ul, підміна IP, регіон, часовий пояс, мова сайту, collected_by, event_key і поля згоди gcs та gcd.

Справжній пейлоуд page_view – рівно такий, яким його склав воркер.

Превʼю sGTM із подією, яку надіслав мій воркер: ліворуч список із пʼяти запитів collect, подія form_deliver, статус згоди з ad_storage та analytics_storage у стані granted, клієнт визначений як Google Analytics: GA4 (Web) - Claimed, а в даних події лежать client_hints, client_id і collected_by зі значенням worker.Превʼю sGTM із подією, яку надіслав мій воркер: ліворуч список із пʼяти запитів collect, подія form_deliver, статус згоди з ad_storage та analytics_storage у стані granted, клієнт визначений як Google Analytics: GA4 (Web) - Claimed, а в даних події лежать client_hints, client_id і collected_by зі значенням worker.

І той самий пейлоуд уже в sGTM: нативний клієнт GA4 забирає його як свій, а collected_by: worker показує, де він зібраний.

Махінації в межах серверного контейнера – то окрема тема, котрої точно буде вдосталь на цьому блозі. Але в контексті моїх вологих веб-аналітичних фантазій, там не те, щоб щось дуже цікаве є розповідати на даному етапі. Все стандартно:

  • Клейм вхідного реквесту GA4
  • Парсинг даних на івентДату
  • Спред і ворвард даних на потрібних нам вендорів

Етап 2: Альтернативний стрімінг сирих даних замість коробочного рішення GA4 ↔ BigQuery

На межі двох епох, між UA і GA4, всі дуже плювалися на нову аналітику (здебільшого, за урізання вже звичного функціоналу і дуже незвичного веб-інтерфейсу), але беззаперечною кіллер-фічею була нативна інтеграція і експорт сирих даних в Google BigQuery. На мою думку це саме той момент, коли маркетологи всього світу щільно насіли на вивчення SQL. І від того моменту я почав забувати, що таке GA4 web-UI (ненавиджу його).

Так склалося, що продукти (в плані бізнеси), з якими я працюю, мають дуже великі обʼєми трафіку і це унеможливлює аналіз на агрегованих даних у вебі. Тож єдиним для нас виходом було переорієнтування на аналіз на основі сирих даних. Та є речі в нативному експорті, котрі мені взагалі не до вподоби:

  • Daily експорт має обмеження в 1 мільйон івентів на добу для одного проперті аналітики;
  • Інтрадей обмежень не має, але там вручну треба процесити дані (теж багато нюансів є);
  • Працюючи виключно на інтрадей експорті, якщо з боку Гугла відбуваються збої в роботі, дані втрачаються назавжди;
  • Процесінг даних на етапі «дані долетіли до ендпоінту ГА4» – «дані поклалися в таблицю», це блекбокс і те ще дно.

Отже, і тут буде своє рішення, з блекджеком і слухами. А для порівняння, забігаючи трохи наперед, на кожному івенті буде передаватись свій event_key, котрий потенційно буде використаний як місток між власним стрімінгом і нативним (на випадок, якщо захочу порівняти власні правила процесингу і повноту даних).

Етап 3: Постпроцесінг (постобробка) даних

Сусідні етапи межують по змісту, тож вже похаяти рідний гугловський процесінг я вже встиг. Не, ну серйозно, я памʼятаю навіть в мережі гуляв спредшит-файл у вигляді таблички, де комʼюніті однодумців збирали баги, повʼязані з сирими даними GA4 при експорті в BigQuery (upd: знайшов).

  • Параметр page_location має gclid, але в окреме поле його чомусь не винесло.
  • google / organic там, де мав би бути google / cpc (то взагалі класика).
  • та інші досадні баги (детальний список за посиланням вище).

Ну, погодьтеся, несолідно червоніти перед менеджером, відповідаючи «то все процесінг» на питання просадки трафіку по платній рекламі. Ні. Я хочу контролювати кожен крок і впливати на дані на кожному з етапів. Отже, правила обробки даних також лягають на мої плечі і логічно діляться на наступні категорії:

  • Атрибуція і все, що з нею повʼязано (source, medium, клік айдішки, групи каналів тощо – все рахується і виводиться на основі page_location & page_referrer)
  • Тачпоінти – логічна сутність між івентом і сесією. Простими словами – доторк ззовні до сайту від користувача. Прийшов з органіки на сайт, повернувся назад у видачу і перейшов вже по рекламі – зробив два тачпоінти: по google / organic і другий – по google / cpc.
  • Пристрій (девайс, браузер, ОС) – парсинг юзер-агента і робота з так званими підказками браузера.
  • Робота з ГЕО полями і консентом
  • Все, що повʼязано з ботами (результатом виноситься вердикт, бот-не-бот)
  • Решта дрібʼязку по типу локального часу користувача і спільного слагу укр і eng сторінки

Після цього етапу маю плоску таблицю на 112 поля. Оскільки трафіку поки нема (ми тільки розганяємося), я дозволив собі розгулятися. Хоча до цього варіантів було в районі 140 полів (але за відчуттями, то вже занадто)

Етап 4: Заглиблення в дата-інженерію

Раніше робота веб-аналітика на тривіальному рівні виглядала так: ставиш ГТМ, запускаєш аналітику, вмикаєш експорт у БігКвері з адмінки ГА4 і все, далі просто нюхаєш квіточки. Зараз до всього цього я пробую себе в якості дата-інженера: будую таке собі ДВХ за всіма канонами, рухаючись порівнево, від сирих даних до вітрин, котрі відповідатимуть на конкретно поставлені мною бізнес-питання:

  • Layer 0 – джерела даних: браузер, кукі, хедери реквестів
  • Layer 1 – raw, складування R2 файлів в Cloudflare (1 файл = 1 хіт, івент) з подальшою склейкою по закриттю повних 24 годин доби до одного файлу
  • Layer 2 – staging, трансформація результату Layer 1 до табличного вигляду в паркет файл з відповідністю 1до1 попереднього етапу.
  • Layer 3 – enriched, місце, де відбувається пост-процесінг, частково описаний в етапі 3. Поки що дійшов лише сюди
  • Layer 4 – models, тут плануються таблиці в розрізі скоупів вище івенту: touchpoints, sessions, pseudo-ids, users, identity_map (словник айдішок, за допомогою котрого будуть відбуватись джойни)
  • Layer 5 – marts, вітрини агрегованих даних, котрі мають відповідати на конкретно поставлені питання, наприклад, про популярність статей

Про існування подібних підходів я десь шось чув, десь шось читав, але ніколи не займався проектуванням леєрів і не проходив степ-бай-степ власноруч. Зізнаюсь, що таски по блогу, повʼязані з дата-інженерією – одні з найулюбленіших на даний момент. Поки сумно, що реальних даних (виключаючи ботів) нема, тож дещо доводиться робити «всліпу» і наперед. Прекрасне ось що: кожен Layer має свої фіксовані правила того, що можна робити, а чого – в жодному разі (наприклад, в жодному випадку не можна змінювати сирі дані, і кожен наступний крок вираховується на основі даних з попереднього кроку).

Етап 5: Вилов / фільтрація ботів і різних краулерів

Якщо ви уважно вдивитесь поміж рядків цієї статті, ви зрозумієте, що я будую самодостатню від GA4 систему аналітики. Так, деякі речі у неї запозичені, по типу параметрів хітів. І, по суті, ніщо не заважає мені взагалі не передавати дані в ГА4.

Але з рішенням будувати кастомний стрімінг і власне сховище на плечах їде і певний трейд-оф: ви б тільки знали, скільки ботів бігає по моєму сайту (як мінімум, я такого не очікував). Навіть на етапі розробки самого сайту, коли я ще не презентував його публічно, в мене генерувалося порядка 1-2к івентів щоденно тільки від ботів. Так, дещо з цього – мої власні автоматичні перевірки, але все ж.

Система простенька – в її основі певний набір правил на основі даних + дві результуючі метрики: bot_signals_against & bot_signals_for. Фактично, це обвинувачення і виправдання факту належності хіта до боту. Виходить такий собі суд, де кожне правило або обвинює, або захищає.

Сигнал Сторона Спрацьовує, коли
tool_name проти юзер-агент називає відомий інструмент або бібліотеку
scan_path проти шлях у запиті схожий на сканування вразливостей
no_user_agent проти юзер-агента немає взагалі
no_language проти accept-language порожній
bot_cipher проти набір шифрів TLS такий, якого браузери не пропонують
hosting_network проти адреса належить мережі-хостеру, і це обвинення враховується тільки коли захисту немає жодного
http3 за реквест прийшов по HTTP/3
browser_facts за приїхала роздільна здатність екрана, тобто скрипт виконав справжній браузер

Останнє «проти» – найобережніше: мережа-хостер сама по собі не доказує нічого.

Одного обвинення без жодного захисту досить, щоб поставити bot_verdict = 'bot'. Одного захисту без обвинень – для 'human'. А коли доводи є з обох боків або немає взагалі, вердикт 'unknown': краще мати хіт, який я не можу класифікувати, ніж людину, записану роботом.

Коли сайт побачить світ і я запущу реальний трафік на нього, є бажання глянути на точність моїх правил порівняно з антиботом від ГА4 шляхом джойну двох датасетів (нативного БК-експорту і мого власного) по ключу event_key.

Етап 6: GA4, як користувач нашими даними, а не їх джерело

Тут може бути дещо трікі. Для 99% (умовно) сайтів світу Гугл аналітика виступає основним джерелом даних для бізнесу, що найменш по роботі з веб-даними. Причини всім відомі – безкоштовність, екосистема Гугл (а багато хто користується Гугл рекламою) і купа інших причин. Чому ГА4 – джерело даних? Бо ми ставимо скрипт, що називається gtag, на фронт-енд сайту і він починає збирати дані з браузера, компонувати їх в хіти-івенти і надсилати в Гугл напряму або в серверний контейнер у тих, хто шарить.

У мене на сайті цього скрипта нема зовсім, і це свідомий вибір – памʼятаєте, що я цілюсь в ЧИСТИЙ серверний трекінг, а не гібридний, де дані народжуються в браузері, а потім пересилаються на серверний контейнер. Як результат, у мене, до прикладу, немає веб контейнера ГТМу в принципі (такий от я збочинець).

Незважаючи на те, що дані я збираю сам на сервері, формат даних на виході виходить такий самий, як з пейлоуду, сформованого гтагом. От і виходить: з сервера я відправляю дані на сГТМ і він, завдяки дефолтному клієнту GA4 без питань приймає мої дані і розкладає їх на івент дату. Далі очевидно – використовуємо тег GA4 для відправки даних і вуаля: ГА4 офіційно стає користувачем даних, котрі МИ створили.

Резюмуючи: тепер ГА4 має вірити моїм даним, а не я даним ГА4. Якщо я кажу, що цей юзер прийшов з Бразилії, значить воно так і є. Якщо я повідомив аналітиці, що цей конкретний користувач не дав згоду себе трекати, то аналітика сприймає це на віру і отримує від мене так званий кукілесс хіт в якості івенту.

У когось може виникнути питання, для чого мені тоді ГА4? По факту ні для чого важливого. Хочу я цього чи ні, але ГА4 – це стандарт ринку. Копатися під капотом і тримати руку на пульсі нововведень – важлива річ для мене, як для професіонала. BI-репорти, як логічний наступний крок, будуть будуватися мною точно в обхід GA4.

Висновок

Тож повертаючись до питання тайтлу. І я б його дещо перефразував: «Чи можливий чистий серверний трекінг» розбив би на «А чи взагалі потрібен чистий?» + «А наскільки чистий серверний трекінг?».

Спойлер – чистого на 100% s2s-трекінгу не може бути. Є деякі речі, котрі серверу ніяк не можуть бути відомі, на кшталт роздільної здатності екрана, часу активної взаємодії і тд.

Дуже суттєвою перевагою «чистого» сервер-сайду є те, що оскільки формування даних відбувається на сервері, нам нестрашні блокувальники реклами.

Основна перевага трекінг-аналітичного сетапу, що я обрав для блогу – незалежність від GA4, її обмежень, постпроцесінгу і можливість обрати власний шлях, котрий навряд буде колись залежати від потенційних «змін на краще» з боку Гугла. Завдяки досвіду і глибокому розумінню підкапотної частини (а також АІ-шці для реверс-інжинірингу), я зміг підтюнити кожен етап до прийнятного саме для мене рівня. Результатом дуже задоволений.

Якщо вам вистачило наснаги дочитати до кінця – дякую. Коментарів поки що нема, але якщо є що сказати / запитати / подискутувати, будь ласка, пишіть в форму – повідомлення прийде мені в телеграм-бота.

Напишіть - все прочитаю і всім відповім.

Або просто напишіть на hello@ozthewizard.com