Content Security Policy: настройка и защита сайта 2026

Content Security Policy — это заголовок HTTP, который я считаю одним из самых недооценённых инструментов защиты в 2026 году. За 10 лет практики я видел, как сайты с отличным кодом ломались из-за отсутствия одной строчки в конфиге nginx.

Что такое CSP и почему без него сайт уязвим

Content Security Policy — это механизм безопасности браузера, который говорит ему, откуда можно загружать ресурсы: скрипты, стили, шрифты, изображения, фреймы. Грубо говоря, вы составляете белый список источников, и всё остальное браузер просто блокирует. XSS-атака? Инжект стороннего скрипта? Без разрешения в CSP это просто не сработает.

У меня был клиент — интернет-магазин на Bitrix, примерно 3000 уникальных в день. Однажды кто-то через уязвимость в форме обратной связи внедрил скрипт, который тихонько собирал данные карт покупателей и отправлял их на сторонний домен. Сайт работал нормально, никаких ошибок. Обнаружили случайно, через месяц, когда банк начал блокировать транзакции. Если бы стоял правильный CSP — скрипт бы не выполнился. Вот цена этой "необязательной" настройки.

По данным отчёта Google за 2025 год, XSS остаётся в топ-3 уязвимостей веб-приложений. И это несмотря на то, что CSP существует с 2012 года. Просто разработчики либо не знают про него, либо боятся сломать что-то в продакшне. Страх понятный, но решаемый — об этом расскажу ниже.

Важно понимать: CSP не замена SSL-сертификату и не замена firewall. Это отдельный слой защиты. Если интересно, как строить комплексную защиту, у меня есть подробный материал про настройку Firewall для сайта — рекомендую читать в связке с этой статьёй.

Как работает CSP: директивы и значения

CSP передаётся через HTTP-заголовок Content-Security-Policy или через мета-тег в HTML. Я всегда рекомендую заголовок — он надёжнее, потому что мета-тег не защищает от некоторых типов атак и его легче обойти.

Директивы — это правила для разных типов ресурсов. Вот основные, которые я использую в 90% проектов:

Значения для директив могут быть разными. 'self' — только с текущего домена. 'none' — вообще ничего. Конкретный URL или домен — только оттуда. 'unsafe-inline' — разрешить инлайн-скрипты и стили (плохая идея, но иногда вынужденная мера). 'unsafe-eval' — разрешить eval() в JavaScript. 'nonce-ЗНАЧЕНИЕ' — разрешить конкретный инлайн-блок по одноразовому токену.

⚠️
Осторожно с unsafe-inline: Если вы пишете 'unsafe-inline' в script-src — вы фактически отключаете защиту от XSS. CSP при этом ещё работает для других директив, но основной смысл теряется. Используйте nonce или hash вместо unsafe-inline везде, где это возможно.

Настройка CSP в nginx: базовый и продвинутый варианты

На большинстве своих проектов я работаю с nginx, поэтому начну с него. Заголовок добавляется в блок server или location.

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

server {
    listen 443 ssl http2;
    server_name example.ru;

    # Базовый CSP
    add_header Content-Security-Policy "
        default-src 'self';
        script-src 'self';
        style-src 'self' 'unsafe-inline';
        img-src 'self' data: https:;
        font-src 'self';
        connect-src 'self';
        frame-src 'none';
        object-src 'none';
        base-uri 'self';
        form-action 'self';
        upgrade-insecure-requests;
    " always;

    # Другие security headers
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}

Но на деле большинство сайтов используют сторонние ресурсы: Google Analytics, Яндекс.Метрику, Google Fonts, YouTube-видео, виджеты обратного звонка, карты. Для реального проекта конфигурация выглядит сложнее. Вот пример для типичного корпоративного сайта на Bitrix с Метрикой и Google Analytics:

add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self'
        'nonce-RANDOM_NONCE_HERE'
        https://mc.yandex.ru
        https://www.google-analytics.com
        https://www.googletagmanager.com
        https://api-maps.yandex.ru
        https://suggest-maps.yandex.ru;
    style-src 'self'
        'unsafe-inline'
        https://fonts.googleapis.com;
    img-src 'self'
        data:
        blob:
        https://mc.yandex.ru
        https://www.google-analytics.com
        https://www.googletagmanager.com
        https://*.maps.yandex.net
        https://*.tile.openstreetmap.org;
    font-src 'self'
        https://fonts.gstatic.com;
    connect-src 'self'
        https://mc.yandex.ru
        https://www.google-analytics.com
        https://api-maps.yandex.ru;
    frame-src
        https://www.youtube.com
        https://www.google.com
        https://yandex.ru;
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    upgrade-insecure-requests;
    report-uri /csp-report;
" always;

Обратите внимание на report-uri /csp-report. Это endpoint, куда браузер будет отправлять JSON-отчёты о нарушениях CSP. Очень полезно при отладке и мониторинге. Про настройку мониторинга в целом я писал отдельно — читайте здесь, там есть хорошие инструменты.

Report-Only режим: как внедрять CSP без риска сломать сайт

Вот главный лайфхак, который я использую всегда, когда добавляю CSP на работающий сайт. Существует заголовок Content-Security-Policy-Report-Only. Он работает точно так же, как обычный CSP, но не блокирует ресурсы — только отправляет отчёты о нарушениях.

Схема такая: сначала включаю Report-Only на 2–4 недели, смотрю отчёты, понимаю, что нужно добавить в белый список, и только потом переключаю на боевой CSP. Это позволяет не сломать ничего в продакшне.

# Шаг 1: только мониторинг, ничего не блокируем
add_header Content-Security-Policy-Report-Only "
    default-src 'self';
    script-src 'self';
    style-src 'self';
    img-src 'self' data: https:;
    font-src 'self';
    connect-src 'self';
    frame-src 'none';
    object-src 'none';
    report-uri /csp-report;
" always;

Для приёма отчётов нужен простой PHP-скрипт. Вот минималистичный вариант, который я использую:

После недели работы в Report-Only режиме у меня обычно набирается список из 20–50 источников, которые нужно добавить в белый список. Это нормально — современные сайты очень зависимы от внешних сервисов. Главное — понимать, что именно добавляешь и зачем.

💡
Совет по nonce: Вместо того чтобы разрешать 'unsafe-inline' для скриптов, используйте nonce. Это случайный токен, который генерируется на каждый запрос и добавляется в тег script. Браузер выполнит только те скрипты, у которых nonce совпадает с указанным в CSP. Это безопасно, потому что атакующий не знает nonce заранее.

CSP для WordPress, Bitrix и Laravel: особенности каждой CMS

WordPress

WordPress — это отдельная боль с точки зрения CSP. Огромное количество плагинов добавляют инлайн-скрипты и стили прямо в HTML, часто без возможности настройки. Редактор Gutenberg сам по себе требует разрешений, которые делают CSP мягче.

Честно говоря, на WordPress я чаще всего иду на компромисс: разрешаю 'unsafe-inline' для style-src (стили), но для script-src стараюсь держать строгий список. Плагин WP Content Security Policy или Headers Security Advanced & HSTS WP помогают управлять заголовками из админки, но я предпочитаю добавлять их через nginx — надёжнее.

Если вы занимаетесь оптимизацией WordPress, то наверняка уже сталкивались с тем, что ускорение WordPress часто требует подключения CDN и внешних ресурсов — всё это нужно прописывать в CSP явно.

Bitrix

На Bitrix ситуация лучше, потому что платформа более предсказуема. Но есть нюансы: компонент карт, виджет онлайн-консультанта из Bitrix24, модуль оплаты — все они тянут внешние ресурсы. Плюс сам Bitrix активно использует inline-скрипты в шаблонах.

На одном из проектов — крупный B2B-портал на Bitrix — я потратил три дня на составление корректного CSP. Особенно мучился с модулем оплаты через CloudPayments: он грузит скрипты с нескольких CDN-доменов, которые ещё и меняются. В итоге пришлось добавить https://*.cloudpayments.ru и https://*.cloudstatic.ru. Если занимаетесь поддержкой Bitrix-проектов — обратите на это внимание.

Если хотите заказать профессиональную настройку безопасности для вашего Bitrix-сайта, посмотрите на услугу поддержки Битрикс — там в том числе настраиваем security headers.

Laravel

Laravel — самое удобное окружение для работы с CSP. Есть отличный пакет spatie/laravel-csp, который позволяет генерировать заголовки программно, с поддержкой nonce прямо в шаблонах Blade.

addDirective(Directive::DEFAULT, Keyword::SELF)
            ->addDirective(Directive::SCRIPT, [
                Keyword::SELF,
                Keyword::NONCE,
                'https://www.googletagmanager.com',
                'https://mc.yandex.ru',
            ])
            ->addDirective(Directive::STYLE, [
                Keyword::SELF,
                Keyword::UNSAFE_INLINE,
                'https://fonts.googleapis.com',
            ])
            ->addDirective(Directive::FONT, [
                Keyword::SELF,
                'https://fonts.gstatic.com',
            ])
            ->addDirective(Directive::IMG, [
                Keyword::SELF,
                'data:',
                'https:',
            ])
            ->addDirective(Directive::OBJECT, Keyword::NONE)
            ->addDirective(Directive::BASE, Keyword::SELF);
    }
}

В Blade-шаблонах можно использовать директиву @cspNonce прямо в теге script — пакет сам подставит nonce и добавит его в заголовок. Это очень элегантно и безопасно.

CSP и аналитика: Google Analytics, Яндекс.Метрика, пиксели

Это самая частая причина, по которой люди боятся включать CSP. Аналитика ломается, пиксели не срабатывают, маркетологи в панике. Но это решаемо.

Для Google Analytics 4 (gtag.js) нужно добавить:

  • script-src: https://www.googletagmanager.com, https://www.google-analytics.com
  • img-src: https://www.google-analytics.com, https://www.googletagmanager.com
  • connect-src: https://www.google-analytics.com, https://analytics.google.com, https://region1.analytics.google.com

Для Яндекс.Метрики:

  • script-src: https://mc.yandex.ru
  • img-src: https://mc.yandex.ru
  • connect-src: https://mc.yandex.ru

Facebook Pixel добавляет ещё несколько доменов: https://connect.facebook.net, https://www.facebook.com. ВКонтакте-пиксель — https://vk.com.

По опыту — если вы подключаете GTM (Google Tag Manager), то через него могут загружаться любые скрипты, и вы теряете контроль над CSP. Это принципиальная проблема: GTM по своей природе конфликтует со строгим CSP. Решение — либо жёстко контролировать, что загружается через GTM, либо выносить критические теги напрямую в код.

ℹ️
Про GTM и CSP: Google Tag Manager поддерживает работу с nonce начиная с 2021 года. Если настроить GTM правильно — передавать nonce через dataLayer — можно сохранить и строгий CSP, и гибкость GTM. Но это требует аккуратной настройки на стороне GTM-контейнера и сервера одновременно.

Дополнительные security headers, которые работают в связке с CSP

CSP — мощный инструмент, но он работает лучше в связке с другими заголовками безопасности. Я всегда настраиваю их вместе.

Strict-Transport-Security (HSTS) — заставляет браузер всегда использовать HTTPS для вашего домена. Без этого атакующий может провести downgrade-атаку и перехватить трафик. Подробнее про переход на HTTPS я писал в чек-листе по HTTPS.

X-Frame-Options — защита от clickjacking. Хотя frame-ancestors в CSP делает то же самое, X-Frame-Options стоит оставить для совместимости со старыми браузерами.

X-Content-Type-Options: nosniff — запрещает браузеру угадывать MIME-тип файла. Без этого атакующий может загрузить HTML-файл под видом картинки и выполнить скрипт.

Permissions-Policy (бывший Feature-Policy) — ограничивает доступ к API браузера: камера, микрофон, геолокация, платёжные данные. Для большинства сайтов я использую такую строку:

add_header Permissions-Policy "
    camera=(),
    microphone=(),
    geolocation=(self),
    payment=(),
    usb=(),
    magnetometer=(),
    gyroscope=(),
    accelerometer=()
" always;

Referrer-Policy — контролирует, какой Referer передаётся при переходе на другой сайт. Я обычно использую strict-origin-when-cross-origin: передаём только домен (без пути) при переходе на другой сайт, и полный URL при переходе внутри сайта.

Проверить все заголовки можно на сервисе securityheaders.com. Он выставляет оценку от F до A+. Цель — минимум A. На проектах, которые я настраиваю, обычно A или A+.

Типичные ошибки при настройке CSP и как их избежать

За годы практики я собрал список граблей, на которые наступают все, кто впервые настраивает CSP.

Ошибка 1: Разрешить всё через wildcard. Встречаю конфиги вида script-src *. Это бессмысленно — вы разрешаете скрипты откуда угодно, CSP не работает. Wildcard можно использовать для img-src или font-src, но для script-src это катастрофа.

Ошибка 2: Забыть про data: URI. Некоторые библиотеки (например, старые версии Bootstrap) используют data: URI для шрифтов или изображений. Если не добавить data: в font-src или img-src — сломается вёрстка.

Ошибка 3: Не тестировать в разных браузерах. Chrome и Firefox реализуют CSP немного по-разному. То, что работает в Chrome, может блокироваться в Firefox. Тестируйте в обоих. Safari отдельная история — там свои нюансы с Service Workers и CSP.

Ошибка 4: Игнорировать inline event handlers. Если в вашем HTML есть onclick="..." или onload="..." — они блокируются строгим CSP. Нужно либо выносить код в отдельные JS-файлы, либо использовать 'unsafe-hashes' с хешем конкретного обработчика. Лучше — выносить в файлы.

Ошибка 5: Не обновлять CSP при добавлении новых интеграций. Подключили новый виджет чата, добавили платёжную систему, поставили новый плагин — и сайт тихо сломался в каком-то браузере. Всегда проверяйте консоль после добавления новых сервисов. Хорошая практика — включать report-uri перманентно, не только при первоначальной настройке.

Если вы хотите, чтобы всё это настроили профессионально и без риска что-то сломать — посмотрите на услугу доработки сайта. Настройка security headers входит в базовый аудит безопасности.

Проверка CSP и постоянный мониторинг

После настройки CSP работа не заканчивается. Нужно регулярно проверять, что ничего не сломалось и нет новых нарушений.

Инструменты для проверки, которые я использую постоянно:

  • securityheaders.com — общая оценка всех security headers
  • csp-evaluator.withgoogle.com — анализ CSP от Google, показывает потенциальные слабые места
  • report-uri.com — коммерческий сервис для сбора и анализа CSP-отчётов с красивым дашбордом
  • Консоль браузера (F12) — самый быстрый способ увидеть нарушения прямо здесь и сейчас

Я рекомендую настроить мониторинг нарушений CSP так же, как вы мониторите ошибки приложения. Если вдруг пошёл поток новых нарушений с незнакомых доменов — это сигнал тревоги. Возможно, кто-то пытается инжектировать скрипты, или один из ваших подрядчиков тихо добавил сторонний трекер без согласования.

Раз в квартал я делаю ревью CSP на своих проектах: проверяю, все ли домены в белом списке ещё актуальны, не появились ли новые зависимости, нет ли избыточных разрешений. Это занимает час, но даёт уверенность, что политика не превратилась в решето за счёт накопленных исключений.

Кстати, если говорить о комплексной безопасности — обязательно почитайте про защиту сайта от взлома. CSP — важная часть, но там много других аспектов, которые работают в связке.

Итого: CSP в 2026 году — это не опциональная фича для параноиков, а базовый гигиенический минимум для любого сайта, который работает с данными пользователей. Настройка занимает от нескольких часов до нескольких дней в зависимости от сложности проекта. Начните с Report-Only режима, соберите данные, постепенно ужесточайте политику. Сломать продакшн при таком подходе практически невозможно. А вот защиту от XSS и data-утечек вы получите реальную.

Хотите защитить сайт с помощью CSP?

Наши специалисты настроят Content Security Policy под ваш проект и обеспечат надёжную защиту от XSS-атак и других угроз.

П
Павел
Веб-разработчик · 10+ лет опыта · Bitrix, WordPress, Laravel

Читайте также

Что такое SSL-сертификат и зачем он нужен сайту Почему сайт не индексируется в Яндекс и Google Редиректы 301 без потери SEO-позиций