Все статьи

Законные Cookie-формы в 2026: свой CMP без серых схем

Старый cookie-баннер с кнопкой «ОК» в Беларуси уже не выглядит безобидной мелочью. Я разбираю, на какие документы реально смотреть в 2026 году и как собрать свой CMP-модуль так, чтобы он не ломал аналитику, маркетинг и здравый смысл.

С cookie в Беларуси у многих до сих пор странная логика: поставили баннер, добавили кнопку «Принять», спрятали ссылку на политику в футере — и вроде закрыли вопрос. В 2026 году это уже плохая ставка. Когда я смотрю на сайты клиентов, проблема почти всегда не в тексте баннера, а в архитектуре: аналитика и рекламные пиксели грузятся раньше выбора пользователя, отказ спрятан глубже согласия, а логов, которые потом можно показать при споре или проверке, просто нет.

На какие документы я бы смотрел первым делом

Если говорить про Беларусь, я бы не начинал с очередного WordPress-плагина и не спорил про «европейский стиль» баннера. Я бы открывал Закон №99-З «О защите персональных данных», Указ №422, примерную политику обработки файлов cookie от НЦЗПД от 24.04.2025, чек-лист НЦЗПД для малого бизнеса от 17.09.2025 и новость НЦЗПД о плане проверок на 2026 год. Из этой связки уже видно, что вопрос давно вышел за пределы «плашки снизу страницы»: НЦЗПД прямо публикует образцы документов, чек-листы типичных нарушений и подтверждает, что в 2026 году продолжит проверки операторов и уполномоченных лиц.

Для cookie тут есть несколько опорных правил. Во-первых, согласие по статье 5 Закона должно быть свободным, однозначным и информированным. До его получения пользователю надо простым языком объяснить цели, состав данных, срок, третьих лиц и последствия выбора, причём отдельно от другой информации. Во-вторых, обязанность доказать факт получения согласия лежит на операторе. Это для меня прямой технический вывод: без серверного журнала согласий и версионирования баннера нормального комплаенса не будет.

Почему старые cookie-плагины в 2026 году уже не тянут

Самое полезное в чек-листе НЦЗПД — он не рассуждает абстрактно, а перечисляет типичные поломки. Там прямо названы проблемы, которые я вижу на сайтах постоянно: баннер без кнопки отказа, блокировка контента до выбора, отсутствие cookie-политики, отсутствие механизма отзыва согласия, усложнённый отказ и визуально «слабая» кнопка отказа. Там же указано, что на баннере должна быть не только кнопка «Принять», но и «Отклонить», а рядом может быть «Настроить». Отдельно НЦЗПД пишет про недопустимость тёмных паттернов — например, когда кнопка согласия специально выделена цветом, размером или формой.

Ещё одна вещь, которую у нас долго игнорировали: молчание не равно согласию. В материалах НЦЗПД есть прямой пример плохой формулы — «продолжая пользоваться сайтом, вы даёте согласие». Для 2026 года это уже не рабочая модель. Если пользователь ничего не нажал, у вас нет согласия на необязательные cookie. Значит, статистика, ретаргетинг, рекламные кабинеты и любые похожие вещи до клика жить не должны.

И ещё один нюанс, который часто пропускают. В чек-листе НЦЗПД сказано, что cookie относятся к персональным данным, а отдельное согласие нужно для необязательных cookie. Если у вас на сайте только технические cookie, согласие не требуется. Это хороший аргумент в пользу собственного CMP: не всё нужно тащить в один баннер и не всё нужно просить «на всякий случай».

Каким я бы делал свой CMP-модуль

Я бы делил модуль на четыре части:

  1. Policy layer — конфиг целей, сроков хранения, перечня сервисов и ссылок на документы.
  2. UI layer — баннер, модалка настроек, кнопка повторного открытия настроек.
  3. Enforcement layer — код, который реально не даёт загрузить необязательные скрипты до выбора.
  4. Audit layer — журнал согласий, версий текста и факта отзыва.

Главная ошибка готовых плагинов — они умеют рисовать интерфейс, но не умеют жёстко управлять загрузкой скриптов. Для НЦЗПД имеет значение не только то, что пользователь видел красивую форму. Имеет значение, что до клика «Принять» или выбора конкретной категории у вас не пошёл запрос в сторонний сервис.

Поэтому я бы проектировал CMP не как баннер, а как маленький state machine. У пользователя есть несколько состояний: unknown, accepted_partial, accepted_all, rejected_all, withdrawn. Любая интеграция — будь то Google Tag Manager, Google Analytics или Яндекс Метрика — должна читать не DOM, а централизованное состояние согласия.

TypeScript
export type ConsentCategory =
  | "necessary"
  | "analytics"
  | "ads"
  | "functional";

export type ConsentState = {
  version: string;
  status: "unknown" | "accepted_partial" | "accepted_all" | "rejected_all" | "withdrawn";
  categories: Record<ConsentCategory, boolean>;
  updatedAt: string | null;
};

export const defaultConsent: ConsentState = {
  version: "2026-04-03",
  status: "unknown",
  categories: {
    necessary: true,
    analytics: false,
    ads: false,
    functional: false,
  },
  updatedAt: null,
};

Тут necessary всегда true, потому что для необходимых cookie согласие не нужно, а всё остальное по умолчанию выключено. Это не «перестраховка программиста», а нормальное следствие логики НЦЗПД: обязательное должно работать для сайта, необязательное — ждать активного действия пользователя.

Почему я не смешиваю аналитику и рекламу в один тумблер

В примерной cookie-политике НЦЗПД целевые cookie показаны общей группой: аналитические, статистические и рекламные. Но в чек-листе есть принцип «одна цель — одно согласие». Я бы читал это консервативно: если у вас реально разные цели, лучше разделить их в интерфейсе. Не потому, что кто-то прямо написал «обязательно сделайте три отдельных переключателя», а потому что так проще доказать информированность и свободу выбора.

На практике это значит:

  • analytics — продуктовая аналитика и измерение трафика;
  • ads — ремаркетинг, рекламные пиксели, lookalike-аудитории;
  • functional — вещи вроде персонализации интерфейса, если они не критичны для работы сайта.

Если проект маленький, можно оставить две опции: necessary и analytics. Если сайт активно использует рекламу, я бы не прятал её внутри «улучшения сервиса».

Frontend: не просто баннер, а жёсткий gatekeeper

Во фронтенде задача простая: не дать сторонним скриптам попасть в страницу раньше времени. Я бы не вставлял <script> для аналитики в index.html и не подключал пиксели через плагины CMS напрямую. Вместо этого — отложенная загрузка после чтения consent state.

TSX
import { useEffect } from "react";
import { useConsentStore } from "./consent-store";

export function AnalyticsBootstrap() {
  const analyticsAllowed = useConsentStore((s) => s.categories.analytics);

  useEffect(() => {
    if (!analyticsAllowed) return;

    const script = document.createElement("script");
    script.src = "https://example-analytics-provider.com/sdk.js";
    script.async = true;
    document.head.appendChild(script);

    return () => {
      script.remove();
      // если SDK умеет cleanup — вызываем его тут
    };
  }, [analyticsAllowed]);

  return null;
}

С рекламными скриптами я бы шёл ещё жёстче: не только не грузил их до согласия, но и при отзыве согласия отправлял бы команду на reset consent mode, удаление клиентских идентификаторов там, где это реально доступно, и остановку дальнейших событий. Закон требует не просто собрать галочку, а прекратить обработку после отзыва согласия, если нет другого законного основания.

Backend: вот где CMP становится взрослым

Серверная часть нужна не для красоты. Она нужна, потому что по закону именно оператор должен доказать, что согласие было. Я бы логировал не только итоговый выбор, но и контекст:

  • версию текста баннера и политики;
  • список категорий;
  • timestamp;
  • IP;
  • user-agent;
  • страну по GeoIP, если используете разные режимы показа;
  • идентификатор пользователя, если он авторизован;
  • hash снимка согласия.

Примерно так:

SQL
create table consent_events (
  id uuid primary key,
  user_id uuid null,
  session_id text not null,
  consent_version text not null,
  action text not null, -- accept_all | reject_all | save_preferences | withdraw
  categories jsonb not null,
  ip inet,
  user_agent text,
  created_at timestamptz not null default now(),
  payload_hash text not null
);

А API может быть совсем простым:

TypeScript
app.post("/api/consent", async (req, res) => {
  const payload = req.body;
  const payloadHash = createHash("sha256")
    .update(JSON.stringify(payload))
    .digest("hex");

  await db.insert("consent_events", {
    session_id: payload.sessionId,
    user_id: req.user?.id ?? null,
    consent_version: payload.version,
    action: payload.action,
    categories: payload.categories,
    ip: req.ip,
    user_agent: req.headers["user-agent"] ?? "",
    payload_hash: payloadHash,
  });

  res.status(204).end();
});

Я бы ещё добавлял таблицу consent_current_state, чтобы быстро читать актуальный выбор, а consent_events оставлял неизменяемым журналом. Для спора с пользователем или для внутреннего аудита именно история событий важнее красивого localStorage.

Политика cookie, третьи лица и трансграничная передача

Свой CMP без документов — это половина решения. НЦЗПД в примерной политике прямо показывает, что для необязательных cookie нужна отдельная политика с категориями, целями, сроками хранения, механизмом изменения настроек и, если вы тянете сторонние сервисы, перечнем третьих лиц. Там же есть важный блок про трансграничную передачу. Если используете иностранные сервисы, надо смотреть не только на SDK, но и на страну назначения, а в случаях без надлежащего уровня защиты — ещё и на информирование о рисках. Для этого у НЦЗПД есть отдельный раздел про трансграничную передачу персональных данных и упоминание приказа директора НЦЗПД от 15.11.2021 №14.

На практике это бьёт по привычке бездумно включать чужие пиксели. Если у вас подключены зарубежные рекламные или аналитические сервисы, CMP должен не просто включать toggle. Он должен быть связан с политикой, перечнем третьих лиц и текстом о рисках там, где это требуется. Иначе форма согласия у вас вроде есть, а юридический смысл у неё дырявый.

Что это значит на практике

Если ты фрилансер или делаешь сайты на аутсорсе, вывод простой: cookie-баннер в 2026 году — это уже не «UI-компонент на сдачу», а отдельный модуль комплаенса. Клиенту нужен не плагин, а связка из интерфейса, блокировки скриптов, политики и журнала согласий.

Если ты владелец сайта, я бы проверил три вещи уже сегодня:

  1. Можно ли отказаться так же быстро, как согласиться.
  2. Грузятся ли аналитика и реклама до выбора пользователя.
  3. Есть ли у тебя лог, который показывает, что именно и когда человек выбрал.

Если ты разработчик продукта, мой совет ещё жёстче: перестань тащить CMP из плагинов «как есть». Свой модуль на React + TypeScript + backend log обычно даёт меньше магии, меньше сюрпризов и лучше переживает аудит. В белорусской практике это особенно заметно, потому что НЦЗПД уже дал и шаблоны, и чек-листы, и прямые примеры того, как выглядит плохая реализация.

Если хочешь, чтобы я посмотрел твой баннер, текущие скрипты и собрал нормальный CMP под твой стек, напиши мне в Telegram или через velutich.com.

VELUTICH.

© 2026 Велютич Дмитрий Игоревич УНП: BE7887089