Я обычно начинаю настройку CSP не с жёсткого запрета, а с режима Report-Only. И честно говоря, это самый спокойный способ понять, что реально грузит ваш сайт, а что сломает его в тот же день, если сразу включить строгую политику.
В 2026 году CSP уже давно не «галочка для аудита», а нормальный рабочий инструмент. У меня на проектах на Bitrix, WordPress и Laravel он помогает ловить инъекции скриптов, левую рекламу, сломанные подключения к CDN и даже неаккуратные вставки от маркетологов. Но если сразу поставить Content-Security-Policy в блокирующий режим, можно похоронить формы, аналитику, личный кабинет и вообще половину сайта. Поэтому я почти всегда начинаю с Report-Only, а потом уже аккуратно ужесточаю политику.
Что такое CSP Report-Only и зачем он нужен
CSP Report-Only — это режим, при котором браузер не блокирует потенциально опасные ресурсы, а только отправляет отчёты о нарушениях. По сути, вы как будто ставите сайт под наблюдение: смотрите, какие скрипты, стили, изображения, iframe, формы и подключения к API не укладываются в будущую политику безопасности.
На практике это очень удобная штука. Например, у одного клиента на WordPress с темой, собранной из десятка плагинов, сначала вылезли нарушения от Google Tag Manager, потом от reCAPTCHA, потом от встроенного чата и ещё от кастомного шрифта с другого домена. Если бы мы включили строгий CSP сразу, сайт бы работал «через раз». А так мы за пару дней собрали список разрешённых источников и спокойно перевели политику в enforce-режим.
Если объяснять грубо говоря, Report-Only — это репетиция перед концертом. А строгий CSP — уже сам концерт, где любая ошибка слышна сразу. И вот на этой репетиции лучше поймать всё: inline-скрипты, старые виджеты, подключение картинок с чужих доменов, iframe от платёжки и даже экзотику вроде blob: или data:.
Кстати, если вы ещё не закрыли базовую безопасность сайта, я бы сначала посмотрел на настройку HTTP-заголовков безопасности, а уже потом на тонкую настройку CSP. Это хорошая связка. И HSTS, и X-Frame-Options, и CSP нормально работают вместе, если их не лепить наугад.
Как работает reporting в 2026 году
Сейчас у браузеров с отчётами по CSP всё уже довольно взрослое. Но есть нюанс: старый механизм report-uri постепенно считается устаревающим, а в приоритете сейчас report-to и связка с Reporting API. На деле поддержка у браузеров не идеальная и я обычно на проектах ставлю оба механизма, чтобы не терять события.
Это особенно важно, если сайт посещают пользователи с разными браузерами и устройствами. Да, Chrome, Edge и современные версии Firefox уже хорошо живут с новыми заголовками. Но у вас могут быть корпоративные браузеры, старые сборки WebView или мобильные клиенты, где логика отчётов ведёт себя не так, как ожидаешь. Поэтому моя рабочая схема — собрать отчёты всеми доступными способами и не надеяться на один единственный формат.
В отчётах вас интересуют не только сами нарушения, но и контекст. Обычно там видно:
- какой директивой было нарушено правило;
- какой URL пытались загрузить;
- с какого документа пришёл запрос;
- был ли это inline-код;
- какой пользовательский агент это словил.
И вот тут начинается нормальная работа. Иногда нарушение — это не баг, а просто особенность интерфейса. Например, у клиента на Laravel был фронт на Vue, который подхватывал inline JSON для инициализации. CSP жаловался на script-src, но блокировать это не надо было — достаточно вынести данные в отдельный endpoint или аккуратно использовать nonce.
/var/log/, чтобы потом быстро фильтровать нарушения по доменам и директивам.Если вам интересна соседняя тема, посмотрите ещё материал про настройку CORS-заголовков. Люди часто путают CORS и CSP, а это вообще разные истории. CORS отвечает за доступ к ресурсам между доменами, а CSP — за то, что ваш браузер вообще имеет право грузить и исполнять.
Подготовка сайта перед включением CSP
Я не советую включать CSP Report-Only на живом сайте без подготовки. Сначала надо понять, какие источники у вас используются. И речь не только про очевидные скрипты и стили. Смотрим шрифты, картинки, iframe, XHR/fetch, WebSocket, видео, карты, аналитики, CRM-формы, антибот-защиту, CDN и всё, что подтягивается снаружи.
У меня был случай на интернет-магазине на Bitrix, где всё красиво открывалось в админке, а на фронте половина картинок шла с поддомена медиа-сервиса. Разработчик даже не помнил, что два года назад туда подключали отдельный файловый хостинг. CSP сразу показал эту дыру. И это хорошо. На деле такие вещи не видно до момента, пока не начнёшь аудит.
Перед настройкой я обычно делаю такой чек-лист:
- собираю список всех внешних доменов;
- проверяю inline-скрипты и inline-стили;
- смотрю, используются ли nonce или hash;
- проверяю формы, API, вебхуки, чаты;
- смотрю, нет ли старых плагинов и виджетов;
- сверяю это с текущими логами браузера.
Если у вас WordPress, особое внимание я бы уделил конструкторам, visual editor и вставкам из плагинов наподобие Elementor, WPBakery, Contact Form 7, WooCommerce, Site Kit, Flamingo и всяким чатам. На Bitrix часто ломают CSP шаблоны, кастомные компоненты и встроенные сервисы аналитики. На Laravel обычно всплывают inline-данные, CDN, сторонние скрипты и фронтенд на React/Vue.
Вот тут уместно почитать и про SSL-сертификаты, и про исправление mixed content. Я видел десятки случаев, когда человек пытался «лечить» CSP, хотя сайт просто грузил HTTP-картинку на HTTPS-странице.
Пример базовой политики Report-Only
Ниже покажу самый приземлённый пример. Это не идеальная финальная политика, а именно стартовая заготовка для сбора отчётов. На ней удобно увидеть, что у вас реально используется в браузере.
add_header Content-Security-Policy-Report-Only "default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self' https://www.google-analytics.com https://region1.google-analytics.com;
frame-src 'self' https://www.youtube.com https://www.google.com;
report-uri /csp-report/;" always;
Да, тут есть 'unsafe-inline'. И да, в 2026 году я всё ещё вижу его на живых проектах. Это не значит, что так надо жить вечно. Но на первом этапе, когда цель — не сломать сайт и собрать отчёты, это допустимый компромисс. Потом лучше перейти на nonce или hash, особенно если у вас корпоративный сайт, интернет-магазин или личный кабинет.
Если сервер у вас на nginx, заголовок можно отдавать прямо из конфигурации. Если Apache — аналогично через .htaccess или vhost. В Bitrix я чаще работаю с nginx + Apache связкой, и там лучше держать CSP в основном конфиге, а не размазывать по проекту. Иначе потом начинаются «а где это прописано?» и «почему на staging одно, а на prod другое».
Для Apache пример будет таким:
<IfModule mod_headers.c>
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://www.googletagmanager.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; report-uri /csp-report/"
</IfModule>
Если же сайт на Laravel, я обычно выношу заголовок в middleware. Это удобнее, чем лепить всё в веб-сервер, особенно когда у проекта несколько окружений и разные наборы доменов для аналитики и интеграций. Кстати, если вы только выстраиваете архитектуру проекта, гляньте ещё статью про Laravel для бизнеса — там я тоже затрагивал вопрос дисциплины в инфраструктуре.
Сбор отчётов и анализ нарушений
Самый важный момент — не просто включить Report-Only, а уметь читать отчёты. И тут я обычно делаю отдельную обработку. Не надо сваливать всё в один длинный лог, который потом никто не анализирует. Лучше разбить по типам нарушений: scripts, styles, images, frame, connect, font.
У меня был клиент, у которого отчёты сначала шли прямо в access.log, потом их пытались парсить вручную. Через неделю стало ясно, что это тупиковый путь. Я сделал отдельный endpoint на PHP 8.2, который принимал JSON-отчёты и складывал их в MySQL 8.0. После этого стало видно, что 80% нарушений — это один и тот же рекламный виджет, который давно пора выкинуть. И его, разумеется, выкинули.
Пример простого обработчика на PHP:
<?php
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!$data) {
http_response_code(400);
exit('bad json');
}
$report = $data['csp-report'] ?? $data['body'] ?? null;
if (!$report) {
http_response_code(400);
exit('no report');
}
$line = json_encode([
'time' => date('c'),
'document_uri' => $report['document-uri'] ?? '',
'violated_directive' => $report['violated-directive'] ?? '',
'blocked_uri' => $report['blocked-uri'] ?? '',
'source_file' => $report['source-file'] ?? '',
'line_number' => $report['line-number'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
], JSON_UNESCAPED_UNICODE);
file_put_contents(__DIR__ . '/csp.log', $line . PHP_EOL, FILE_APPEND | LOCK_EX);
http_response_code(204);
Да, это пример попроще, без базы. Но для старта он отличный. Потом можно отправлять данные в очередь, в Redis или напрямую в таблицу. Если у вас уже настроены задачи и фоновые процессы, полезно связать это с cron jobs или системой логирования. А если инфраструктура чуть сложнее, я бы ещё присмотрелся к настройке логов сайта и мониторингу ошибок.
Что смотреть в первую очередь:
- какие домены встречаются чаще всего;
- какие нарушения идут только на мобильных;
- ломаются ли формы и отправка данных;
- не появляется ли
unsafe-evalбез причины; - нет ли постоянных жалоб на inline-скрипты в старых шаблонах.
И вот тут важно не впадать в фанатизм. Если отчёт показывает, что 10 раз за день срабатывает скрипт аналитики, это одно. Если срабатывает левый домен, который вообще не должен быть на сайте, это уже совсем другая история. Второе — это красный флаг, и я бы на таком проекте ещё раз посмотрел на защиту сайта от взлома.
Nonce, hash и отказ от unsafe-inline
Когда отчёты собраны, наступает нормальная инженерная часть. И вот здесь уже начинается чистка. Если у вас много inline-скриптов, однозначно стоит перейти на nonce или hash. Просто оставить 'unsafe-inline' навсегда — плохая идея. Да, сайт будет работать. Но смысл CSP при этом заметно теряется.
На моей практике nonce особенно хорошо заходит на проектах, где шаблоны генерируются сервером. Например, Laravel или Bitrix с продуманной архитектурой. Для WordPress тоже можно, но там всё зависит от темы и плагинов. Иногда проще заменить один проблемный плагин, чем городить костыли вокруг inline-стилей.
Пример CSP с nonce:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id' https://www.googletagmanager.com; style-src 'self' 'nonce-$request_id' https://fonts.googleapis.com; img-src 'self' data: https:; connect-src 'self'; frame-src 'self' https://www.youtube.com;" always;
Тут, конечно, я намеренно показал упрощённую схему. В реальной жизни nonce нужно генерировать в приложении, а не надеяться на магию nginx. И это как раз тот случай, когда веб-разработчик должен понимать весь стек: PHP, веб-сервер, шаблоны, кэш, CDN. Если у вас это разъехалось, никакой CSP не спасёт. Тогда лучше сначала навести порядок в техническом долге сайта.
Hash хорош тогда, когда inline-код статичен. Если кусок JS одинаковый и не меняется от страницы к странице, хэш — вполне рабочий вариант. Но если код генерируется динамически, например подставляет ID пользователя или токен, nonce удобнее. На деле выбор простой: статичный фрагмент — hash, динамический — nonce.
Типичные ошибки при настройке в 2026 году
Самая распространённая ошибка — включить Report-Only и вообще не смотреть отчёты. Ну серьёзно, это бесполезно. Вторая ошибка — пытаться сразу сделать политику «идеальной», перекрыть всё до последнего и удивляться, почему у сайта отвалилась аналитика, чат и платёжка. Третья — смешать в кучу CSP, CORS, HSTS и mixed content, а потом искать виноватого в nginx.
Ещё одна классика — забыть про сторонние сервисы. Например, у вас установлен чат, онлайн-оплата, CRM-форма, карты, видео с YouTube, виджет отзывов, антибот-защита и шрифты с Google Fonts. И потом вы удивляетесь, что сайт весь в нарушениях. Да потому что внешних точек интеграции слишком много. Если проект крупный, я бы ещё отдельно посмотрел на интеграцию CRM с сайтом — там часто всплывают неожиданные домены и скрипты.
На стороне сервера тоже бывают косяки. Например, если CSP задан и в nginx, и в Apache, браузер видит обе политики. А это уже может давать странные пересечения. Или заголовок ставится, но CDN его переписывает. Или на staging политика одна, а на production — другая. Поэтому я всегда проверяю заголовки через:
curl -I https://site.ru;- DevTools в браузере;
- онлайн-проверку response headers;
- логи сервера и приложения.
Если замечаете, что после внедрения отчётов у вас выросло количество ошибок на фронте, не паникуйте. Иногда это не CSP, а просто совпадение с обновлением темы, плагина или ядра CMS. Я бы в такой момент смотрел ещё и на версионирование и откат обновлений. На нормальном проекте всё это должно быть связано со staging, бэкапами и планом отката.
Рабочий план внедрения без боли
Если нужен мой практический план, я обычно иду так. Сначала ставлю Content-Security-Policy-Report-Only на staging. Потом гоняю сайт минимум несколько часов, а лучше сутки. Проверяю десктоп, мобильные, формы, корзину, личный кабинет, админку, страницы с медиа и страницу контактов. После этого смотрю отчёты и собираю все домены в список.
Дальше разделяю всё на три группы: можно оставить, надо заменить, надо удалить. И вот только после этого включаю полноценную CSP на проде. Если проект большой, делаю это поэтапно: сначала только report-only, потом мягкая политика, потом усиление и чистка inline-кода. Это медленно, зато без пожара. На моей практике быстрые внедрения CSP почти всегда заканчиваются ночными правками и нервными созвонами.
Если сайт уже живёт на HTTPS, у вас настроен редирект 301, есть бэкапы и staging, процесс идёт спокойно. Кстати, очень советую держать под рукой материалы про правильный бэкап сайта и staging-сайт для безопасной проверки обновлений. Это та база, без которой безопасность превращается в лотерею.
И ещё момент. Если у вас интернет-магазин, я бы не забывал про тестирование на фоне боевого трафика. Иногда CSP ломает только конкретную платёжную страницу или только мобильный браузер Safari. Такое сложно заметить без полноценного прогрева. Тут очень выручает автоматическое тестирование сайта и обычная человеческая проверка руками.
Что в итоге я делаю на реальных проектах
Если коротко, я почти никогда не ставлю CSP сразу в жёсткий режим. Сначала Report-Only, потом анализ нарушений, потом чистка внешних источников, потом nonce/hash и только после этого блокировка. На сайтах с живым трафиком, особенно на Bitrix и WordPress, это единственный здравый подход. Всё остальное — это либо авантюра, либо лишняя работа для техподдержки.
Для сайта в 2026 году CSP Report-Only — это не временная игрушка, а нормальный этап внедрения безопасности. Он помогает увидеть реальную картину, не ломая пользователей, не портя SEO и не устраивая пожар в аналитике. И да, если делать всё аккуратно, можно добиться хорошего баланса между защитой и стабильностью. На деле это и есть взрослая разработка.
Если вам нужна помощь с безопасностью, заголовками, серверной конфигурацией или внедрением CSP на WordPress, Bitrix или Laravel, я обычно такие задачи беру в работу как часть поддержки Bitrix, поддержки WordPress или общей доработки сайта. И лучше это делать не по принципу «сломалось — починим», а нормально, с планом и отчётами.
Хотите внедрить CSP Report-Only без поломок?
Начните с режима отчётов, чтобы безопасно выявить нарушения политики и подготовить сайт к полноценной защите.
