Даталог
Esc

Начни вводить для поиска...

    Маркетинговая аналитика · 12 минут чтения · Практик

    UTM-разметка: почему у тебя бардак и как навести порядок раз и навсегда

    Реальные проблемы UTM: legacy, редиректы, мэтчинг фронта и бэка, нормализация, мониторинг каналов. SQL-запросы для ClickHouse и PostgreSQL.

    Открой отчёт по источникам. Видишь google, Google, GOOGLE, google.com и гугл — всё как отдельные строки? Это ещё цветочки. Настоящий UTM-ад начинается, когда ты приходишь в компанию, где «у нас так заведено», и пытаешься из этого хаоса собрать хоть какую-то аналитику.

    Масштаб проблемы: не только регистр

    Все статьи про UTM начинаются с «используйте нижний регистр». Спасибо, капитан. А что делать, когда:

    • У тебя 5 команд, и каждая размечает по-своему уже 2 года
    • Legacy-метки невозможно поменять задним числом, потому что на них завязаны отчёты для директора
    • Редиректы на сайте молча убивают твою разметку
    • Бизнес хочет видеть каналы paid_performance и paid_brand, а не cpc и cpm

    Вот об этом и поговорим. Без воды, по порядку.

    Топ-5 ошибок: быстрая диагностика

    1. Разный регистр

    UTM-параметры регистрозависимы. utm_source=Google и utm_source=google — две строки в отчёте. Всегда нижний регистр, без исключений.

    2. Нет единой конвенции

    Один пишет email, другой e-mail, третий newsletter. Без зафиксированного стандарта каждый будет творить что хочет. И будет прав — ты же не дал правил.

    3. Кириллица и пробелы

    utm_campaign=весенняя распродажа — кошмар. Пробелы превращаются в %20, кириллица кодируется. Только латиница, только дефисы.

    4. Нет utm_campaign

    Многие ставят только source и medium. Ты знаешь, что трафик из Яндекс.Директ, но не знаешь из какой кампании. Бюджет «размазан», оптимизировать нечего.

    5. UTM на внутренних ссылках

    Ставишь UTM на баннер внутри сайта — перезаписываешь оригинальный источник. Человек пришёл из рекламы, кликнул по баннеру на главной, и теперь его источник — internal_banner. Атрибуция сломана. Для внутренних переходов используй отдельные параметры, не UTM.

    Проблемы, о которых молчат курсы

    Команды не понимают, зачем это нужно

    Не все участники маркетинга осознают проблему. Маркетолог запускает кампанию с utm_source=facebook, таргетолог пишет Facebook, агентство — fb. Три строки вместо одной, бюджет «размывается».

    И если ты пришёл в компанию, где уже кто-то что-то размечал до тебя — готовься к legacy. На вопрос «почему у вас utm_medium=paid-social-fb-retarget?» ты услышишь: «у нас так заведено». Это нормально. Ненормально — оставить всё как есть.

    Редиректы убивают UTM-метки

    Об этом точно никто не пишет. Представь: пользователь переходит на сайт, попадает на страницу авторизации, редирект бросает его в личный кабинет. URL выглядит так:

    https://site.ru/login?return_to=https://site.ru/dashboard?utm_source=yandex&utm_medium=cpc

    UTM-метки вложены в параметр return_to, а не стоят на верхнем уровне URL. Яндекс.Метрика их просто игнорирует. Хит уходит на сервер без разметки, и трафик падает в (direct).

    Правильно

    site.ru/page?utm_source=yandex&utm_medium=cpc

    UTM на верхнем уровне

    Метрика видит: yandex / cpc

    Неправильно

    site.ru/login?return_to=…?utm_source=yandex

    UTM вложен в параметр

    Метрика видит: (direct) / (none)

    Увеличить
    Слева — UTM работает. Справа — UTM потерян из-за вложенности в redirect-параметр.

    Правильно

    site.ru/page?utm_source=yandex&utm_medium=cpc

    UTM на верхнем уровне

    Метрика видит: yandex / cpc

    Неправильно

    site.ru/login?return_to=…?utm_source=yandex

    UTM вложен в параметр

    Метрика видит: (direct) / (none)

    Слева — UTM работает. Справа — UTM потерян из-за вложенности в redirect-параметр.

    Стандарты Метрики ≠ потребности бизнеса

    Яндекс.Метрика рекомендует свои стандарты разметки для красивых отчётов «Источники, сводка». Но если бизнесу нужны каналы paid_performance, paid_brand, crm_email, partner_referral — стандарты Метрики не подойдут.

    И это нормально. Бизнес отвечает на вопрос «как больше заработать», а не «как красиво выглядит в стандартных отчётах». Данные по выручке, сделкам, транзакциям живут на бэкенде, и именно их нужно мэтчить с фронтовой разметкой.

    Конвенция: бери и внедряй

    ПараметрФорматПример
    utm_sourceплатформа, lowercaseyandex, google, vk, telegram
    utm_mediumтип трафикаcpc, cpm, email, social, referral
    utm_campaignYYYYMM_категория_название202603_promo_spring-sale
    utm_contentформат или вариантbanner_1200x628, text_v2
    utm_termключевое словоkupit_krossovki

    Правила:

    • Только нижний регистр, только латиница
    • Разделитель слов — дефис (-), разделитель сегментов — нижнее подчёркивание (_)
    • Дата в начале campaign для сортировки
    • Никаких пробелов, кириллицы, спецсимволов
    utm_source=yandex&utm_medium=cpc&utm_campaign=202603_promo_spring
    sourceОткуда пришёл: площадка, платформа
    mediumКак пришёл: cpc, email, social
    campaignЗачем: конкретная кампания + дата
    Увеличить
    Каждый параметр отвечает за свой уровень атрибуции
    utm_source=yandex&utm_medium=cpc&utm_campaign=202603_promo_spring
    sourceОткуда пришёл: площадка, платформа
    mediumКак пришёл: cpc, email, social
    campaignЗачем: конкретная кампания + дата

    Каждый параметр отвечает за свой уровень атрибуции

    Как привести всё в порядок: алгоритм

    1Стандарт
    2Генератор
    3Маппинг
    4Ответственные
    5SQL-нормализация
    6Бизнес-каналы
    7Тесты
    Увеличить
    7 шагов от хаоса к порядку — каждый обязателен
    1Стандарт
    2Генератор
    3Маппинг
    4Ответственные
    5SQL-нормализация
    6Бизнес-каналы
    7Тесты

    7 шагов от хаоса к порядку — каждый обязателен

    1. Зафиксируй стандарт. Одна страница в Confluence / Notion / Google Docs. Таблица выше — минимум. Отправляй каждому новому подрядчику до начала работы.

    2. Сделай генератор. Google-таблица с выпадающими списками и формулой, которая собирает URL. Никто не вбивает UTM руками. Или разверни генератор на внутреннем сайте — решение любое, главное чтобы было одно.

    3. Напиши маппинг для legacy. Функция или словарь, который превращает старые метки в новый стандарт. Да, это ручная работа. Да, без неё никак.

    4. Назначь ответственных. Один-два человека, к которым команды обращаются с вопросами «как размечать». Без ответственного стандарт умрёт через месяц.

    5. Нормализуй на уровне SQL. Все отчёты строятся через функции нормализации, а не на сырых данных.

    6. Присвой бизнес-каналы. Поверх нормализованных UTM — бизнес-логика: paid_performance, paid_brand, organic, crm_email и т.д. Именно эти каналы видит бизнес.

    7. Повесь тесты. Мониторинг доли каналов в динамике. Аномалия = кто-то снова накосячил.

    SQL: находим бардак и нормализуем

    Сначала масштаб бедствия — все вариации одного источника:

    Найди дубликаты utm_source
    -- PostgreSQL: дубликаты utm_source по нижнему регистру
    SELECT
    lower(utm_source) AS source_normalized,
    array_agg(DISTINCT utm_source) AS variants,
    COUNT(DISTINCT utm_source) AS variant_count,
    COUNT(*) AS total_sessions
    FROM marketing_events
    WHERE utm_source != ''
    AND event_date >= CURRENT_DATE - INTERVAL '90 days'
    GROUP BY source_normalized
    HAVING COUNT(DISTINCT utm_source) > 1
    ORDER BY total_sessions DESC;

    Этот запрос покажет, что google имеет варианты ['Google', 'google', 'GOOGLE', 'google.com'] — 4 варианта. А теперь представь это по каждому источнику.

    Создаём словарь нормализации:

    Словарь маппинга: legacy → стандарт
    -- PostgreSQL: таблица нормализации
    CREATE TABLE utm_normalization (
    raw_value TEXT PRIMARY KEY,
    normalized_value TEXT NOT NULL
    );
    INSERT INTO utm_normalization VALUES
    ('Google', 'google'), ('GOOGLE', 'google'),
    ('google.com', 'google'), ('Facebook', 'facebook'),
    ('FB', 'facebook'), ('fb', 'facebook'),
    ('ВКонтакте', 'vk'), ('vkontakte', 'vk');

    Чистый отчёт с бизнес-каналами:

    Нормализованный отчёт с бизнес-каналами
    -- PostgreSQL: отчёт с бизнес-каналами
    SELECT
    coalesce(n.normalized_value, lower(m.utm_source)) AS source_clean,
    lower(m.utm_medium) AS medium_clean,
    CASE
    WHEN lower(m.utm_medium) IN ('cpc', 'ppc') AND lower(m.utm_campaign) LIKE '%brand%'
    THEN 'paid_brand'
    WHEN lower(m.utm_medium) IN ('cpc', 'ppc')
    THEN 'paid_performance'
    WHEN lower(m.utm_medium) = 'email' THEN 'crm_email'
    WHEN lower(m.utm_medium) IN ('social', 'smm') THEN 'organic_social'
    WHEN m.utm_source = '' AND m.utm_medium = '' THEN 'direct'
    ELSE 'other'
    END AS business_channel,
    COUNT(DISTINCT m.user_id) AS users,
    COUNT(*) FILTER (WHERE m.event_type = 'purchase') AS purchases,
    SUM(m.revenue) FILTER (WHERE m.event_type = 'purchase') AS revenue
    FROM marketing_events m
    LEFT JOIN utm_normalization n ON m.utm_source = n.raw_value
    WHERE m.event_date >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY source_clean, medium_clean, business_channel
    ORDER BY revenue DESC;

    Попробуй: найди дубликаты UTM

    SQL
    Загрузка WASM...

    Мониторинг: ловим аномалии до того, как они сломают отчёт

    Доля other
    8%
    Норма
    Доля direct
    22%
    Внимание
    Новые UTM
    14
    За неделю
    paid 42%organic 28%direct 22%other 8%
    Увеличить
    Три ключевые метрики, которые нужно отслеживать еженедельно
    Доля other
    8%
    Норма
    Доля direct
    22%
    Внимание
    Новые UTM
    14
    За неделю
    paid 42%organic 28%direct 22%other 8%

    Три ключевые метрики, которые нужно отслеживать еженедельно

    Алгоритм мониторинга:

    1. Собери референс. Возьми 3-6 месяцев данных с корректной разметкой. Посчитай среднюю долю визитов на каждый бизнес-канал.

    2. Следи за долей other. Канал other — это всё, что не смэтчилось по правилам. Если его доля больше 15-20% — проблема. Если растёт — кто-то снова косячит.

    3. Вешай тесты на пороги. Если доля канала вырастает на 20% и более от среднего — это аномалия. Лезь в сырые данные и смотри, что попадает в utm_medium, utm_source, utm_campaign конкретного канала.

    Мониторинг доли каналов по неделям
    -- PostgreSQL: мониторинг доли каналов по неделям
    WITH channel_stats AS (
    SELECT
    date_trunc('week', event_date)::date AS week,
    CASE
    WHEN lower(utm_medium) IN ('cpc', 'ppc') THEN 'paid'
    WHEN lower(utm_medium) = 'email' THEN 'email'
    WHEN lower(utm_medium) IN ('social', 'smm') THEN 'social'
    WHEN utm_source = '' THEN 'direct'
    ELSE 'other'
    END AS channel,
    COUNT(*) AS visits
    FROM marketing_events
    WHERE event_date >= CURRENT_DATE - INTERVAL '180 days'
    GROUP BY week, channel
    ),
    weekly_totals AS (
    SELECT week, SUM(visits) AS total
    FROM channel_stats GROUP BY week
    )
    SELECT
    cs.week, cs.channel, cs.visits,
    ROUND(cs.visits::numeric / wt.total * 100, 1) AS share_pct
    FROM channel_stats cs
    JOIN weekly_totals wt ON cs.week = wt.week
    ORDER BY cs.week DESC, share_pct DESC;

    Хаос → Порядок: до и после

    Хаос
    Google / cpc → 1,200
    google / CPC → 800
    google.com / paid → 350
    Гугл / cpc → 150
    Нормализация
    Порядок
    google / cpc / paid_performance → 2,500
    1 строка. Чистые данные. Верные решения.
    Увеличить
    Нормализация превращает 4 строки мусора в 1 строку правды
    Хаос
    Google / cpc → 1,200
    google / CPC → 800
    google.com / paid → 350
    Гугл / cpc → 150
    Нормализация
    Порядок
    google / cpc / paid_performance → 2,500
    1 строка. Чистые данные. Верные решения.

    Нормализация превращает 4 строки мусора в 1 строку правды

    До (хаос):

    utm_sourceutm_mediumsessions
    Googlecpc1,200
    googleCPC800
    google.compaid350
    Гуглcpc150

    Четыре строки. Ты думаешь, что Google приносит 1,200 визитов. На самом деле — 2,500.

    После (порядок):

    sourcemediumbusiness_channelsessions
    googlecpcpaid_performance2,500

    Одна строка. Чистая картина. Правильные решения.

    Динамические параметры: автоматизируй или страдай

    Ручная UTM-разметка — это путь в ад при масштабе. Яндекс.Директ и Google Ads умеют подставлять значения автоматически через макросы.

    Яндекс.Директ

    МакросЧто подставит
    {campaign_id}ID кампании
    {ad_id}ID объявления
    {keyword}Ключевое слово
    {source}Площадка (домен сайта РСЯ или none для поиска)
    {source_type}Тип площадки: search или context
    {position_type}Тип блока: premium, other, none

    Пример: ?utm_source=yandex&utm_medium={source_type}&utm_campaign={campaign_id}&utm_content={ad_id}&utm_term={keyword}

    МакросЧто подставит
    {campaignid}ID кампании
    {creative}ID объявления
    {keyword}Ключевое слово
    {network}Сеть: g (поиск), d (КМС), y (YouTube)
    {matchtype}Тип соответствия: e, p, b

    Пример: ?utm_source=google&utm_medium=cpc&utm_campaign={campaignid}&utm_content={creative}&utm_term={keyword}

    Атрибуция: кому засчитать конверсию

    Ты навёл порядок в UTM. Нормализовал, замэппил, повесил бизнес-каналы. И тут приходит вопрос: пользователь за 2 недели зашёл 5 раз из разных каналов — кому засчитать покупку?

    Это задача атрибуции, и она заслуживает отдельной статьи. Но вот суть: большинство компаний используют Last Paid Click — конверсия засчитывается последнему платному касанию. Если платных не было — последнему значимому бесплатному (органика, SMM). Если и таких нет — Direct.

    Last Paid Click: SQL-ранжирование
    -- PostgreSQL: LPC-атрибуция с приоритетами
    SELECT customer_id, event_type, channel_group, visit_datetime,
    ROW_NUMBER() OVER (
    PARTITION BY customer_id, event_type
    ORDER BY
    CASE
    WHEN channel_group IN ('paid_performance', 'paid_display', 'smm_paid') THEN 1
    WHEN channel_group NOT IN ('direct', 'other') THEN 2
    ELSE 3
    END,
    visit_datetime DESC
    ) AS rn_lpc
    FROM user_visits
    WHERE visit_datetime >= event_datetime - INTERVAL '90 days';
    -- Берём rn_lpc = 1

    Подробнее — в статье про модели атрибуции в SQL: Last Click, Last Paid Click с 3-уровневым приоритетом, линейная, U-shape, Time Decay — с готовыми запросами и интерактивными примерами.

    Как не допустить отката

    1. Генератор UTM. Google-таблица / внутренний сервис с выпадающими списками. Никто не вбивает UTM руками.

    2. Валидация. Если есть свой трекер — проверяй UTM при приёме. Отвергай кириллицу, заглавные, пробелы.

    3. Регулярный аудит. Запускай SQL-запрос для поиска дубликатов раз в неделю. Новый мусор появляется, когда подключаются подрядчики.

    4. Документация. Одна страница с правилами + генератор. Отправляй до начала работы.

    5. Санкции. Звучит жёстко, но без последствий стандарт не будут соблюдать. Точки контроля в процессах — обязательно.

    FAQ

    UTM-метки не передаются в Яндекс.Метрику. Что проверить?

    Три вещи: 1) метки стоят на верхнем уровне URL, а не внутри параметров редиректа, 2) нет JavaScript-редиректов, которые теряют query string, 3) счётчик Метрики загружается до редиректа.

    Можно ли использовать свои параметры вместо utm_?

    Можно, но Яндекс.Метрика и GA4 распознают только стандартные utm_source, utm_medium, utm_campaign, utm_content, utm_term. Свои параметры придётся обрабатывать на уровне SQL из сырых данных или Logs API.

    Как быть с legacy-метками: переделывать или маппить?

    Маппить. Менять старые метки задним числом невозможно — данные уже собраны. Создай словарь raw → normalized и применяй в SQL. Новые метки — по стандарту. Подробнее про работу с SQL-воронками на размеченных данных — в статье про анализ воронок.

    Какой процент канала other считается нормальным?

    Ниже 10% — хорошо. 10-20% — терпимо, но надо разбираться. Больше 20% — проблема с разметкой. Смотри в динамике: если доля растёт — значит кто-то снова размечает не по стандарту.

    Нужен ли UTM для SEO-трафика?

    Нет. Поисковый трафик определяется автоматически по referer. Если повесить UTM на органическую ссылку — перезапишешь источник и испортишь данные по SEO. UTM только для платного и контролируемого трафика.

    Как отследить, что подрядчик разметил неправильно?

    Запрос на поиск дубликатов (выше в статье) + мониторинг доли other. Если после запуска новой кампании other вырос — смотри сырые данные: что туда попало, от кого, с какими метками. Подробнее про построение мониторинговых дашбордов — в статье про DataLens.

    Итого

    UTM-разметка — не скучная бюрократия и не «базовый навык из курса». Это фундамент, без которого вся аналитика — от отчётов по воронкам до дашбордов — построена на песке. Стандарт, генератор, маппинг, бизнес-каналы, тесты — делай один раз, экономь часы каждую неделю. А когда кто-то скажет «у нас так заведено» — покажи ему долю канала other в 40% и спроси, нравится ли ему принимать решения на мусорных данных.

    Нравится контент? Подписывайся на Telegram

    SQL-задачки, кейсы из практики и закулисье аналитики.

    Подписаться @datalog_blog
    Поделиться:

    Была ли статья полезной?

    Похожие статьи

    Подпишись на Telegram-канал

    Разборы SQL, аналитические кейсы, шаблоны дашбордов и полезные инструменты. Без воды.

    Подписаться @datalog_blog

    Горячие клавиши

    j / k
    Следующая / предыдущая статья
    / Открыть поиск
    b Сохранить в закладки
    ? Показать горячие клавиши

    Горячие клавиши не работают при вводе текста