Реализация Cookie Consent Banner для сайта (GDPR)
Cookie consent banner — компонент, который получает информированное согласие пользователя перед установкой необязательных cookies. Требование GDPR (статья 7) и ePrivacy Directive для всех сайтов, доступных пользователям ЕС.
Классификация cookies
По GDPR cookies делятся на категории:
| Категория | Описание | Требует согласия |
|---|---|---|
| Strictly Necessary | Сессия, CSRF, корзина | Нет |
| Functional | Языковые настройки, тема | Обычно нет |
| Analytics | Google Analytics, Metrika | Да |
| Marketing | Facebook Pixel, ремаркетинг | Да |
Технические требования к баннеру
- Согласие до установки cookies (не после)
- Отказ должен быть так же прост, как согласие
- Раздельное согласие по категориям (granular consent)
- Возможность отозвать согласие в любой момент
- Хранение записи о согласии (consent record) с timestamp
Реализация через Cookiebot / OneTrust
Готовые решения для соответствия GDPR:
Cookiebot:
<script id="Cookiebot" src="https://consent.cookiebot.com/uc.js"
data-cbid="YOUR-CBID"
data-blockingmode="auto"
type="text/javascript">
</script>
Auto-blocking mode автоматически блокирует скрипты трекинга до получения согласия, сканирует cookies на сайте.
Кастомная реализация (React)
// CookieConsentContext.tsx
interface ConsentState {
analytics: boolean;
marketing: boolean;
functional: boolean;
}
const CookieConsentContext = createContext<{
consent: ConsentState;
updateConsent: (updates: Partial<ConsentState>) => void;
hasResponded: boolean;
}>({} as any);
export function CookieConsentProvider({ children }: { children: ReactNode }) {
const [consent, setConsent] = useState<ConsentState>({
analytics: false,
marketing: false,
functional: false,
});
const [hasResponded, setHasResponded] = useState(false);
useEffect(() => {
const stored = localStorage.getItem('cookie_consent');
if (stored) {
const data = JSON.parse(stored);
setConsent(data.preferences);
setHasResponded(true);
}
}, []);
const updateConsent = (updates: Partial<ConsentState>) => {
const newConsent = { ...consent, ...updates };
const record = {
preferences: newConsent,
timestamp: new Date().toISOString(),
version: '1.0',
};
localStorage.setItem('cookie_consent', JSON.stringify(record));
setConsent(newConsent);
setHasResponded(true);
// Отправить запись на сервер
fetch('/api/consent', {
method: 'POST',
body: JSON.stringify(record),
});
};
return (
<CookieConsentContext.Provider value={{ consent, updateConsent, hasResponded }}>
{children}
</CookieConsentContext.Provider>
);
}
// CookieBanner.tsx
export function CookieBanner() {
const { hasResponded, updateConsent } = useContext(CookieConsentContext);
const [showDetails, setShowDetails] = useState(false);
const [selections, setSelections] = useState({
analytics: false, marketing: false, functional: true,
});
if (hasResponded) return null;
return (
<div className="cookie-banner" role="dialog" aria-label="Cookie consent">
<p>Мы используем cookies для улучшения работы сайта.</p>
{showDetails && (
<div className="cookie-categories">
<label>
<input type="checkbox" checked disabled />
Необходимые (всегда активны)
</label>
<label>
<input type="checkbox"
checked={selections.analytics}
onChange={e => setSelections(s => ({...s, analytics: e.target.checked}))}
/>
Аналитика
</label>
<label>
<input type="checkbox"
checked={selections.marketing}
onChange={e => setSelections(s => ({...s, marketing: e.target.checked}))}
/>
Маркетинг
</label>
</div>
)}
<div className="cookie-actions">
<button onClick={() => updateConsent({ analytics: false, marketing: false })}>
Только необходимые
</button>
<button onClick={() => setShowDetails(!showDetails)}>
Настроить
</button>
<button className="primary"
onClick={() => updateConsent({ analytics: true, marketing: true, functional: true })}>
Принять все
</button>
{showDetails && (
<button onClick={() => updateConsent(selections)}>
Сохранить выбор
</button>
)}
</div>
</div>
);
}
Блокировка скриптов до согласия
// Загрузка Google Analytics только после согласия
export function useAnalytics() {
const { consent } = useContext(CookieConsentContext);
useEffect(() => {
if (consent.analytics && !window.gtag) {
const script = document.createElement('script');
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`;
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
window.gtag = function() { window.dataLayer.push(arguments); };
window.gtag('js', new Date());
window.gtag('config', GA_ID, { anonymize_ip: true });
}
}, [consent.analytics]);
}
Хранение записей о согласии
По GDPR нужно хранить доказательства согласия:
// migrations/create_consent_records_table.php
Schema::create('consent_records', function (Blueprint $table) {
$table->id();
$table->string('user_identifier'); // IP + fingerprint или user_id
$table->json('preferences');
$table->string('version');
$table->ipAddress('ip_address');
$table->timestamp('consented_at');
$table->timestamp('withdrawn_at')->nullable();
});
Срок реализации
- Готовое решение (Cookiebot/CookieYes): 1 день
- Кастомный баннер с блокировкой скриптов: 3–5 дней
- Хранение consent records + API: +1 день







