Рабочий разбор
Главная мысль
Если серверные покупки в GA4 поголовно уходят в «(not set)», не спешите винить передачу данных. Сначала сравните формат client_id серверного события и клиентского - чаще всего идентификаторы просто не совпадают, и покупки склеиваются с «новым» пользователем без истории.
Контекст
У клиента оплата разнесена во времени с визитом: заказ оформляется на сайте, а деньги приходят позже, иногда через несколько дней. Поэтому покупку в аналитику отправляет не браузер, а бэкенд - вебхуком на серверный GTM в момент фактической оплаты.
Всё выглядело настроенным правильно. Событие purchase доходило до GA4, несло сумму, номер заказа, состав корзины. Но в отчёте по источникам все оплаты до единой сидели в «(not set)» по каналу сессии и в «(direct) / (none)» по первому источнику пользователя. Пятьсот с лишним транзакций, и ни одной с нормальным каналом.
Обычная первая реакция - «разработчик шлёт не те данные», тут не сработала.
Где ломается логика
GA4 привязывает событие к сессии по двум идентификаторам сразу: client_id (какой браузер) и session_id (какая сессия). Источник трафика живёт в событии session_start конкретной сессии. Если серверное событие приходит с идентификатором, который не совпадает с тем, что был у пользователя в браузере, GA4 не находит нужную сессию и заводит нового пользователя без истории. Такой пользователь появляется сразу с покупки, поэтому его источник - «(direct) / (none)», а канал сессии - «(not set)».
Дальше начинается перебор ложных версий. Сначала кажется, что не передаётся session_id - добавили, не помогло. Потом - что виновата отложенная оплата и закрытая сессия. Это правда влияет, но объясняет не всё: если бы дело было только в тайминге, часть быстрых оплат всё равно подтянула бы источник. А тут в «(direct)» схлопнулись сто процентов заказов. Такое единообразие - сигнал, что покупки уходят не тому пользователю, а не что сессия истекла.
Проверили, что именно шлёт бэкенд. Оказалось, он отдаёт корректный client_id - тот самый, что лежит в браузерной cookie _ga, в числовом виде. То есть на стороне разработчика всё чисто.
А вот в самом событии, которое уходило в GA4, client_id был другим - строкой-хэшем с тем же временным хвостом. Число превращалось в хэш где-то внутри серверного контейнера.
| Где смотрим | Формат client_id |
|---|---|
Что шлёт бэкенд (cookie _ga) | 1234567890.1700000000 - число |
| Клиентские события (view_item, add_to_cart) | 1234567890.1700000000 - число |
| Что уходит в GA4 с серверным purchase | …hash…=.1700000000 - хэш |
Виновата настройка GA4-клиента: режим идентификации стоял «управление сервером». В этом режиме контейнер берёт входящий client_id и хэширует его в собственный серверный идентификатор. Клиентские события при этом продолжают жить на браузерном client_id из _ga. Получаются два разных идентификатора на одного человека, которые между собой не сходятся.
Рабочая модель
Решение - переключить GA4-клиент в серверном контейнере с «управления сервером» на «управление через JavaScript». Тогда контейнер перестаёт хэшировать входящий идентификатор и использует браузерный client_id как есть. После переключения client_id серверной покупки стал числовым и совпал с идентификатором клиентских событий того же человека. Покупки начали склеиваться с сессиями, источник подтянулся.
Как это проверить руками, не гадая. Нужен серверный предпросмотр контейнера - тогда видно сырое входящее тело вебхука и то, каким идентификатор уходит дальше. Без предпросмотра ни логи, ни отчёты GA4 хэширование не показывают: client_id не выводится как обычный параметр события.
Как применить
Что подготовить: доступ к серверному контейнеру, рабочий предпросмотр (для него нужен отдельный сервис предпросмотра, у боевого сервера должен быть прописан его адрес), доступ к отчётам или выгрузке в хранилище.
Что проверить первым: сравнить client_id серверной покупки и клиентского события одного пользователя. Если форматы разные (число против хэша) - причина найдена, дело в режиме идентификации, а не в разработчике.
Какой результат считать полезным: у новых оплат client_id стал числовым и совпадает с браузерным, а в отчётах по свежим покупкам появляется реальный канал вместо сплошного «(not set)».
Когда остановиться: как только новые покупки склеиваются с сессиями и источник виден. Старые данные не переписываются - в аналитике они иммутабельны, поэтому исторические «(not set)» так и останутся. Фикс работает только вперёд.
Ограничения
Переключение режима идентификации - это размен. Браузерный client_id живёт в cookie, которую Safari режет до семи дней. Для отложенных оплат в Safari покупка снова может не склеиться, если пользователь вернулся позже и cookie уже сбросилась. Частично это лечится тем, что бэкенд сохраняет client_id в момент оформления заказа - тогда даже при сбросе cookie позже покупка несёт зафиксированный идентификатор.
Ещё нюанс: при смене режима идентификации несколько дней в отчётах будет всплеск «новых пользователей», пока перестраиваются cookie. Это ожидаемо, пугаться не нужно.
И не додумывайте атрибуцию сильно отложенных оплат. Если сессия давно закрыта, даже с правильным client_id канал конкретной сессии подтянуть не всегда получится - тогда опираются на первый источник пользователя, а не на источник сессии покупки.
Следующий полезный шаг
Проверьте свой серверный GA4-клиент прямо сейчас: в каком режиме идентификации он работает. Если стоит «управление сервером», а покупки вы шлёте с браузерным client_id - вы, скорее всего, уже теряете источник и просто этого не видите.
Источники
- Google Analytics, официальная документация по серверному тегированию и идентификации client_id (проверено 09.2026).
- Simo Ahava, практический разбор server-side client_id и FPID: как серверный контейнер формирует и хэширует идентификатор (проверено 09.2026).
Пока тихо. Можно начать.
Проверяем доступность комментариев…