Обзор — что делает этот экран
Рядом с числом уникальных посетителей на каждой карточке Сервисы ▸ Веб расположены два значка, каждый из которых открывает модальное окно.
| Значок | Окно | Источник |
|---|---|---|
| Человек | Аналитика посетителей | Google Analytics 4 (Analytics Data API) |
| Лупа | Поисковый трафик | Google Search Console (Search Analytics API) |
Если сведения о доступе nginx_meta_ct на карточке говорят вам, что получил ваш сервер, то эти два окна говорят, откуда пришли посетители и какие поисковые запросы их привели, — таких сведений в журналах вашего сервера попросту нет.
То, что Site Kit делает в WordPress, по существу сводится к тому, чтобы собрать за вас запросы к отчётам. В Laravel вы собираете их сами и получаете те же самые числа, поэтому мы не используем ни плагин, ни
google/apiclient: вся работа — «подписать JWT закрытым ключом и обменять его на токен доступа», а сервер подписывает это средствами самого языка. Тащить огромное дерево зависимостей ради двух REST-эндпоинтов — плохая сделка.
Когда это нужно
- Трафик на карточке вырос, и вы хотите знать, какой канал и какой источник его дали.
- Вам нужны поисковые запросы, приносящие показы и клики, и то, на какую страницу ведёт каждый запрос.
- Вы хотите одним взглядом оценить состав аудитории — долю новых посетителей, состав устройств, характер привлечения.
- Вы отслеживаете показы, клики и среднюю позицию после публикации материала.
Устройство экрана
Оба окна разделены на вкладки Обзор / Запросы / Трафик (вкладка запросов есть только в окне поиска), так что вы переключаетесь, а не прокручиваете. Выбор периода предлагает 7 / 28 / 90 дней — ровно тот набор, который разрешён на сервере.
Аналитика посетителей (GA4)
- Шесть KPI: посетители, новые посетители, сеансы, просмотры страниц, доля вовлечённых сеансов, средний сеанс — каждый с изменением относительно непосредственно предшествующего периода.
- Три полосы состава: возвращаемость посетителей (новые/вернувшиеся), состав устройств (компьютеры против телефонов и планшетов), характер привлечения (поиск против прочего). Все три — деления надвое, поэтому у них единый визуальный язык, а поскольку абсолютные числа уже несут KPI, здесь показывается только доля.
- График динамики: посетители, сеансы и просмотры страниц.
- Шесть панелей (вкладка «Трафик»): каналы, источники, города, страны, устройства, популярные страницы.
Канал и источник — не одно и то же. Канал — это сгруппированный ответ (
Organic Search), источник — конкретный (naver,github.com). Особенно для корейских сайтов naver против google — это две совершенно разные истории привлечения.
Поисковый трафик (Search Console)
- Пять KPI: клики, показы, CTR, средняя позиция, число запросов.
- Облако слов: размер и цвет кодируют показы, ● отмечает запросы, давшие реальные клики, а числа — это показы
~1d / ~7d / all. - Таблица запросов: клики, показы, CTR, позиция.
- Страница входа × запрос: разверните страницу, чтобы увидеть запросы, по которым на неё заходили.
- Три панели: страницы входа, страны, устройства.
Средняя позиция взвешена по показам. При простом среднем один показ на первой позиции компенсирует десять тысяч показов на сороковой, что грубо приукрашивает число.
Как это настроить
1. Создайте сервисный аккаунт (Google Cloud)
- Google Cloud Console → выберите или создайте проект → IAM и администрирование ▸ Сервисные аккаунты → Создать сервисный аккаунт. Роль на уровне проекта не требуется.
- Откройте аккаунт → Ключи → Добавить ключ ▸ Создать ключ ▸ JSON → скачайте файл.
- В разделе API и сервисы ▸ Библиотека включите Google Analytics Data API и Google Search Console API.
2. Зарегистрируйте его в InfraGuard
В разделе Сервисы ▸ Веб ▸ Управление сайтами ▸ вкладка редактирования сайта, в блоке Интеграция с Google:
| Поле | Значение | Где найти |
|---|---|---|
| Идентификатор ресурса GA4 | только цифры (например, 550688232) |
Администратор GA4 ▸ Сведения о ресурсе либо URL #/a<account>p<property>/ — часть после p |
| Ресурс Search Console | sc-domain:example.com или https://example.com/ |
в точности та строка, что показана в списке ресурсов Search Console |
| JSON сервисного аккаунта | вставьте скачанный файл целиком | — |
Идентификатор ресурса GA4 — это не идентификатор аккаунта. В
#/a405285491p550688232/часть послеa— это аккаунт, а часть послеp— ресурс. Google намеренно не отличает «такого ресурса нет» от «нет прав» (само существование ресурса уже является сведением), поэтому неверный номер выглядит в точности как проблема с правами. Сначала проверьте идентификатор ресурса.
Никогда не выводите ресурс Search Console из домена. Только тот, кто подтвердил права, знает, ресурс это домена (
sc-domain:) или префикс URL (https://…/), а неверный выбор возвращает пустой результат, а не ошибку.
3. Пригласите сервисный аккаунт в каждый ресурс
JSON — это лишь удостоверение личности; доступ выдаётся для каждого ресурса отдельно. Скопируйте client_email, показанный после сохранения, а затем:
| Ресурс | Где | Роль |
|---|---|---|
| GA4 | Администратор ▸ Управление доступом к ресурсу ▸ + |
Читатель |
| Search Console | Настройки ▸ Пользователи и разрешения ▸ Добавить пользователя | Полный доступ |
В GA4 это управление доступом к ресурсу, а не к аккаунту (левый столбец — аккаунт, средний — ресурс). В Search Console нужен полный доступ, а не ограниченный: Search Analytics API отклоняет пользователей с ограниченными правами. У сервисных аккаунтов нет почтового ящика, поэтому снимите флажок «уведомить по электронной почте».
4. Проверьте подключение
[Проверить подключение] в окне управления один раз обращается к обоим API (за последние 7 дней), используя сохранённые учётные данные, и сообщает результат по каждой части отдельно.
| Состояние | Значение |
|---|---|
ok |
Google ответил. Ноль строк — это тоже ok: новый сайт и отставание GSC не являются ошибками |
fail |
Формулировка самого Google плюс client_email, который нужно пригласить |
skip |
Ресурс не настроен, поэтому шаг пропущен (это не сбой) |
Если учётных данных нет вовсе, не выполняется ни одного вызова (ноль исходящих запросов). Несохранённые изменения не проверяются — сначала сохраните.
Правила точности — почему числа считаются именно так
Они следуют ловушкам, уже доказанным опытом Site Kit, и каждая опирается на факты.
| Правило | Причина |
|---|---|
| Окно заканчивается вчерашним днём | Сегодняшний день агрегирован лишь частично, поэтому его включение всегда обрушивает правый край |
GA4 читает totals[0] из metricAggregations:['TOTAL'] |
Суммирование строк, к которым применён limit, занижает итоговое число |
Фильтр по списку hostName в каждом запросе к GA4 |
Один ресурс часто охватывает несколько хостов, и без фильтра карточка показывает весь трафик ресурса. Мы передаём все домены, которыми владеет сервис (основной, дополнительные, www), чтобы точно совпасть с агрегацией vts на карточке |
В рейтингах используются keepEmptyRows:false + notExpression{emptyFilter} |
Значение (not set) в GA4 регулярно обгоняет настоящие города и источники |
Во временных рядах используется keepEmptyRows:true |
День без трафика не должен исчезать с оси |
GSC использует dataState:'all' плюс отставание около 2 дней |
Без этого последние дни читаются как нули, что выглядит как обвал трафика |
Ресурсы домена ограничиваются через page contains |
sc-domain: охватывает все поддомены, поэтому просачиваются запросы соседних сайтов |
Источники данных
| Объект | Подключение / расположение | Роль |
|---|---|---|
infr_web_service.ga4_property_id · gsc_site_url |
default (000771) | сопоставление ресурса для каждого сайта |
infr_web_service.google_cred_enc · google_cred_email |
default (000772) | сервисный аккаунт для каждого сайта (Crypt, только запись) |
Analytics Data API properties/{id}:runReport |
внешний | отчёты по посетителям |
Search Console searchAnalytics/query |
внешний | отчёты по поисковому трафику |
Сервисы: Services\Google\{GoogleTokenService, GoogleReportService, SiteInsightService}; эндпоинты Service\WebController@insight{Visitors,Search,Test}.
Кэширование: ответы отчётов кэшируются, и ключ кэша включает действующую личность (client_email). Тот же URL и то же тело запроса, отправленные под другим аккаунтом, — это другая авторизация, поэтому общий ключ отдал бы одному сайту кэшированный ответ другого.
Почему учётные данные задаются для каждого сайта
Единый глобальный ключ в .env здесь работать не может. Зарегистрированные сайты принадлежат разным владельцам, а значит и разным аккаунтам Google, и один сервисный аккаунт читает только те ресурсы, в которые его пригласили: как только зарегистрированы сайты двух заказчиков, хотя бы один всегда будет давать отказ. Отсюда и поле, куда вставляют ключ для каждого сайта.
- Хранится через
Crypt(APP_KEY) и только на запись: модель скрывает шифротекст, поэтому интерфейс видит лишь логический признак и несекретныйclient_email. Закрытый ключ обратно не выдаётся. - При сохранении выполняются три проверки: JSON разбирается, присутствуют
client_email/private_keyи проверка подписи принимает ключ. Обрезанные вставки встречаются часто, и если пропустить такую, позже она проявится как ложное «отказано в доступе», из-за которого вы будете рыться в IAM у Google. - Общесерверные учётные данные сохраняются как запасной вариант для установок с одним аккаунтом; собственные учётные данные сайта всегда важнее.
Поле учётных данных передаётся, только если вы его тронули. Форма никогда не получает закрытый ключ обратно, поэтому трактовка «не передано» как «очистить» стирала бы учётные данные каждый раз, когда кто-то правит одну лишь заметку. Явная очистка — единственный способ удаления.
Права доступа
- Чтение: любой вошедший пользователь (
infr.auth, уровень ≥ 1). Экран только читает внешнюю статистику и ничего не меняет ни в InfraGuard, ни в Google, поэтому здесь нет ни повторного подтверждения, ни записи в аудит (та же дисциплина, что и у проверки репутации IP). - Сохранение учётных данных и ресурсов:
user_level >= 2(как и сохранение сайта), с записью вinfr_audit_log.
Цель задаётся только идентификатором id зарегистрированного сайта — идентификатор ресурса нельзя передать параметром. Если бы вызывающая сторона могла назвать ресурс, права нашего сервисного аккаунта превратились бы в способ читать чужие ресурсы GA4 и Search Console (та же дисциплина защиты от SSRF, что и у живых проверок в Каталоге). Период точно так же ограничен на сервере значениями 7/28/90.
Диагностика · Частые вопросы
- На карточке нет значков — чтобы кнопки появились, у сайта должны быть и учётные данные, и сопоставление ресурса. Проверка выполняется для каждого сайта, поэтому сайт с собственными учётными данными работает даже при пустом глобальном
.env. Сохраните и перезагрузите страницу. - Все проверки зелёные, но всё равно возвращается 0 — это проблема сбора, а не запроса. ① Окно заканчивается вчерашним днём, поэтому тег, установленный сегодня, ещё вне него. ② Убедитесь, что идентификатор отслеживания (
G-…), который сайт действительно отдаёт, относится к запрашиваемому ресурсу (Администратор GA4 ▸ Потоки данных); если данные попадают в другой ресурс, вы будете вечно читать ноль. ③ Search Console отстаёт примерно на 2 дня, а у нового сайта показов может ещё не быть вовсе. User does not have sufficient permissions— либо пропущен шаг 3 (приглашение аккаунта), либо неверен идентификатор ресурса GA4. Формулировка в обоих случаях одинакова, поэтому сначала проверьте идентификатор ресурса.- Пустой ответ приходит только от Search Console — скорее всего строка ресурса не соответствует действительности (
sc-domain:илиhttps://…/). Сбой выражается пустым результатом, а не ошибкой, поэтому сверьте её со списком ресурсов. - После сохранения учётные данные пропали — сохранение с очищенным полем является явным удалением. Если поле не трогать, значение сохраняется.
- В окне показан значок
Sample— данные ещё не собраны, поэтому показан иллюстративный пример, чтобы можно было оценить вёрстку. Как только данные появятся, окно автоматически переключится на настоящие.
Смежные разделы
- Сервисы ▸ Веб — сам экран веб-мониторинга (карточки, доступность, сертификаты)
- Публичная посадочная страница и аналитика посетителей — сторона сбора данных (gtag/GTM, подтверждение прав в Search Console)
- Проверка репутации IP — та же модель прав для внешнего запроса только на чтение
