На практике CSP позволяет явно описать, какие источники браузеру разрешено использовать для загрузки скриптов, стилей, изображений, шрифтов, iframe, AJAX-запросов и других ресурсов. Если источник не разрешён политикой — браузер его заблокирует.
Простыми словами: CSP — это белый список допустимых источников контента, который помогает браузеру отсеивать опасный или нежелательный код ещё до его выполнения.

Зачем нужна Content Security Policy
Грамотно настроенная CSP помогает решить сразу несколько задач безопасности:
- снизить риск XSS-атак;
- ограничить выполнение стороннего JavaScript;
- запретить загрузку ресурсов с подозрительных доменов;
- контролировать iframe, AJAX, изображения, шрифты и медиа;
- получать отчёты о нарушениях политики и находить слабые места в шаблоне сайта.
Особенно полезен CSP на проектах, где есть сторонние виджеты, рекламные вставки, онлайн-чаты, счётчики аналитики, кастомные скрипты, legacy-шаблоны и старые CMS, в которых исторически накопилось много небезопасных inline-конструкций.
Какой подход к CSP сегодня считается правильным
Одна из типовых ошибок — пытаться сразу собрать огромную “простыню” из десятков доменов, разрешить 'unsafe-inline', 'unsafe-eval' и на этом считать CSP настроенным. Формально заголовок будет работать, но реальная защитная ценность такой политики часто оказывается очень низкой.
Более профессиональный подход состоит из трёх этапов:
- сначала включаем Content-Security-Policy-Report-Only и собираем отчёты;
- после анализа убираем лишние источники и выстраиваем белый список;
- по возможности переходим к более строгой модели через nonce или hash для inline-скриптов и стилей.
Важно: чем меньше в вашей политике 'unsafe-inline', 'unsafe-eval' и избыточных внешних доменов, тем выше реальная ценность CSP.
Какие директивы CSP используются чаще всего
Ниже — самые полезные директивы, которые обычно нужны в реальных проектах.
- default-src — источник по умолчанию для всех типов ресурсов, если не указана более узкая директива.
- script-src — разрешённые источники JavaScript.
- style-src — разрешённые источники CSS.
- img-src — разрешённые источники изображений.
- font-src — разрешённые источники шрифтов.
- connect-src — разрешённые источники fetch / XHR / WebSocket / Beacon.
- frame-src — разрешённые источники iframe и frame.
- object-src — загрузка плагинов и встроенных объектов; чаще всего лучше ставить
'none'. - base-uri — ограничение использования тега
<base>. - form-action — куда разрешено отправлять формы.
- frame-ancestors — кто может встраивать ваш сайт в iframe.
- upgrade-insecure-requests — попытка перевести HTTP-ресурсы на HTTPS.
- report-uri / report-to — отправка отчётов о нарушениях.
Базовый пример CSP для сайта через .htaccess
Если сайт работает на Apache и вы хотите быстро задать аккуратную базовую политику, можно начать с такого варианта:
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'; img-src 'self' data: https:; font-src 'self' https: data:; style-src 'self' 'unsafe-inline' https:; script-src 'self' https:; connect-src 'self' https:; frame-src 'self' https:; upgrade-insecure-requests"
</IfModule>
Это не “идеально строгий” вариант, но уже нормальная стартовая база для большинства сайтов. Он:
- разрешает ресурсы в первую очередь только с собственного сайта;
- запрещает опасные встроенные объекты через
object-src 'none'; - ограничивает
base-uriиform-action; - не даёт произвольно встраивать страницу куда угодно через
frame-ancestors; - разрешает только HTTPS-источники для внешних подключений.
Режим Report-Only: правильный старт внедрения CSP
Если вы не уверены, что боевой CSP сразу не поломает часть сайта, безопаснее начинать с режима отчётов. В таком случае браузер не блокирует ресурсы, а только сообщает о нарушениях политики.
<IfModule mod_headers.c>
Header always set Content-Security-Policy-Report-Only "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'; img-src 'self' data: https:; font-src 'self' https: data:; style-src 'self' 'unsafe-inline' https:; script-src 'self' https:; connect-src 'self' https:; frame-src 'self' https:; report-uri /csp/report.php"
</IfModule>
Такой режим особенно удобен на старых проектах, где заранее неизвестно, какие внешние ресурсы реально используются шаблоном, виджетами, рекламой, аналитикой и CMS-плагинами.
Рекомендация: сначала 1–2 недели собирайте нарушения в Report-Only, затем чистите политику и только после этого переводите её в боевой Content-Security-Policy.
Пример более строгой CSP через nonce
Если вы хотите повысить уровень защиты, лучше уходить от массового использования 'unsafe-inline' и переходить к nonce — одноразовому токену, который сервер генерирует на каждый ответ и вставляет в разрешённые inline-скрипты или стили.
В этом случае браузер выполнит только те inline-скрипты, у которых nonce совпадает со значением в CSP-заголовке.
Пример на PHP
<?php
declare(strict_types=1);
$nonce = rtrim(strtr(base64_encode(random_bytes(16)), '+/', '-_'), '=');
header(
"Content-Security-Policy: " .
"default-src 'self'; " .
"base-uri 'self'; " .
"object-src 'none'; " .
"frame-ancestors 'self'; " .
"form-action 'self'; " .
"img-src 'self' data: https:; " .
"font-src 'self' https: data:; " .
"style-src 'self' 'nonce-{$nonce}' https:; " .
"script-src 'self' 'nonce-{$nonce}' https:; " .
"connect-src 'self' https:; " .
"frame-src 'self' https:; " .
"upgrade-insecure-requests"
);
?>
Далее этот же nonce добавляется в разрешённый inline-скрипт:
<script nonce="<?= htmlspecialchars($nonce, ENT_QUOTES, 'UTF-8') ?>">
window.appConfig = {
env: 'production'
};
</script>
Такой подход заметно сильнее, чем использование 'unsafe-inline', потому что браузер не выполнит произвольный внедрённый inline-код без правильного nonce.
Когда допустимы unsafe-inline и unsafe-eval
В реальной жизни полностью убрать их сразу получается не всегда. На старых CMS, шаблонах, конструкторах, самописных административных панелях и виджетах часто остаются inline-скрипты, inline-стили и куски кода, завязанные на eval-подобное поведение.
Но важно понимать разницу:
'unsafe-inline'— ослабляет защиту и позволяет встроенный код;'unsafe-eval'— ещё хуже с точки зрения безопасности и должен убираться в первую очередь, если это возможно;nonceиhash— более безопасная альтернатива для inline-кода.
Критично: если вы ставите в CSP одновременно широкие allowlist, 'unsafe-inline' и 'unsafe-eval', то формально политика есть, но реальная защитная ценность может быть значительно ниже, чем кажется.
Как подключить CSP через PHP
Если вам удобнее управлять заголовком на уровне приложения, а не через Apache, CSP можно задавать прямо в PHP-коде.
<?php
declare(strict_types=1);
header(
"Content-Security-Policy: " .
"default-src 'self'; " .
"base-uri 'self'; " .
"object-src 'none'; " .
"frame-ancestors 'self'; " .
"form-action 'self'; " .
"img-src 'self' data: https:; " .
"font-src 'self' https: data:; " .
"style-src 'self' 'unsafe-inline' https:; " .
"script-src 'self' https:; " .
"connect-src 'self' https:; " .
"frame-src 'self' https:; " .
"upgrade-insecure-requests; " .
"report-uri /csp/report.php"
);
Такой вариант особенно удобен, если политика должна зависеть от окружения, домена, режима разработки, наличия nonce или особенностей конкретного шаблона.
Какие источники обычно приходится добавлять
На реальном сайте CSP почти всегда приходится подстраивать под фактические внешние сервисы. Чаще всего это:
- системы аналитики;
- онлайн-чаты и callback-виджеты;
- видеовставки YouTube или RuTube;
- карты и геосервисы;
- CDN для библиотек, шрифтов, иконок;
- платёжные формы и iframe сторонних систем.
Но добавлять хосты надо не “на всякий случай”, а только по фактическим нарушениям или по осознанной необходимости. Чем короче и чище белый список — тем лучше.
Безопасный обработчик отчётов CSP на PHP
Для анализа нарушений удобно принимать отчёты в отдельный PHP-файл. Но здесь важно не просто “сваливать всё в txt”, а делать это аккуратно: валидировать JSON, ограничивать размер, не исполнять содержимое, логировать безопасно и писать данные в нормальный формат.
Ниже — аккуратный минимальный пример обработчика /csp/report.php:
<?php
declare(strict_types=1);
http_response_code(204);
header('Content-Type: text/plain; charset=UTF-8');
header('X-Content-Type-Options: nosniff');
$raw = file_get_contents('php://input');
if ($raw === false || $raw === '') {
exit;
}
if (strlen($raw) > 1024 * 1024) {
exit;
}
$data = json_decode($raw, true);
if (!is_array($data)) {
exit;
}
$report = $data['csp-report'] ?? $data;
if (!is_array($report)) {
exit;
}
$payload = [
'time' => gmdate('c'),
'document_uri' => (string)($report['document-uri'] ?? ''),
'referrer' => (string)($report['referrer'] ?? ''),
'violated_directive' => (string)($report['violated-directive'] ?? ''),
'effective_directive' => (string)($report['effective-directive'] ?? ''),
'blocked_uri' => (string)($report['blocked-uri'] ?? ''),
'source_file' => (string)($report['source-file'] ?? ''),
'line_number' => (int)($report['line-number'] ?? 0),
'column_number' => (int)($report['column-number'] ?? 0),
'status_code' => (int)($report['status-code'] ?? 0),
'script_sample' => mb_substr((string)($report['script-sample'] ?? ''), 0, 500),
];
$dir = __DIR__ . '/logs';
if (!is_dir($dir)) {
mkdir($dir, 0750, true);
}
$file = $dir . '/csp-' . gmdate('Y-m-d') . '.jsonl';
$fp = fopen($file, 'ab');
if ($fp !== false) {
try {
if (flock($fp, LOCK_EX)) {
fwrite(
$fp,
json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL
);
fflush($fp);
flock($fp, LOCK_UN);
}
} finally {
fclose($fp);
}
}
Такой вариант лучше простого текстового лога, потому что:
- не пытается слепо хранить всё подряд;
- ограничивает размер входящих данных;
- нормализует поля отчёта;
- пишет в удобный формат JSONL;
- не зависит от вывода HTML и безопаснее для последующего анализа.
Как разбирать отчёты CSP
Когда вы включаете Report-Only, через некоторое время у вас появляются данные о том, что именно нарушает политику. Обычно самые полезные поля в отчёте:
- document-uri — на какой странице сработало нарушение;
- blocked-uri — какой источник был заблокирован;
- violated-directive — какое правило нарушено;
- effective-directive — какую конкретно директиву надо править;
- source-file, line-number — где искать проблему в коде;
- script-sample — фрагмент подозрительного inline-кода, если браузер его передал.
Важно не добавлять новый домен автоматически в белый список только потому, что он появился в отчёте. Сначала нужно понять, это действительно нужный ресурс сайта или мусор, расширение браузера, заражённый клиент, сторонняя подмена или попытка атаки.
Типовые ошибки при настройке CSP
- сразу включать жёсткий боевой CSP без режима
Report-Only; - разрешать слишком много доменов “на всякий случай”;
- без необходимости использовать
'unsafe-inline'и'unsafe-eval'; - не ограничивать
object-src,base-uri,form-action; - не разделять
script-src,style-src,img-src,connect-src; - строить политику только по шаблону из интернета без анализа реальных подключений сайта;
- автоматически добавлять всё из отчётов в allowlist без ручной проверки;
- хранить отчёты небезопасно и потом выводить их без экранирования.
Практический чек-лист внедрения CSP
- Определите, какие внешние сервисы реально используются на сайте.
- Соберите стартовую политику с базовыми директивами.
- Включите
Content-Security-Policy-Report-Only. - Соберите и разберите нарушения.
- Уберите лишние источники и разделите директивы по типам ресурсов.
- По возможности сократите inline-код.
- Переведите скрипты и стили на
nonceилиhash. - Только после этого включайте боевой
Content-Security-Policy.
Хорошая стратегия: сначала добиться чистого и понятного Report-Only, затем постепенно ужесточать политику. Так CSP становится не формальной галочкой, а реально полезным слоем защиты.
Когда CSP особенно полезен
CSP даёт наибольшую пользу в следующих сценариях:
- старые CMS и шаблоны с большим количеством inline-кода;
- сайты с большим числом внешних виджетов и аналитики;
- проекты, где важна защита от XSS и внедрения стороннего JS;
- личные кабинеты, формы, корзины, административные панели;
- сайты, где уже были инциденты с подозрительными подключениями или инъекциями.
Вывод
Content Security Policy — это не просто “ещё один HTTP-заголовок”, а реально полезный слой защиты сайта. Но максимальный эффект он даёт только тогда, когда политика строится осознанно: с разделением директив, с анализом нарушений, с минимизацией лишних источников и, по возможности, с переходом на nonce или hash вместо бесконтрольного 'unsafe-inline'.
Для большинства сайтов лучший путь такой: сначала включить Content-Security-Policy-Report-Only, собрать отчёты, вычистить политику, а затем перевести её в боевой режим. Именно так CSP становится действительно полезной частью системы безопасности, а не просто длинной строкой в конфиге.
FAQ
Можно ли просто скопировать чужую CSP и вставить себе?
Можно использовать чужой пример как отправную точку, но готовая политика почти всегда требует адаптации под ваш шаблон, внешние сервисы, CMS, аналитику, виджеты и фронтенд.
Нужно ли сразу включать боевой режим?
Обычно нет. Намного безопаснее сначала включить Content-Security-Policy-Report-Only, собрать нарушения и только потом переходить к реальному блоку ресурсов.
Что лучше: .htaccess или PHP?
Если политика статичная и единая для всего сайта — удобно через .htaccess. Если нужны nonce, условия, разное поведение по окружениям и шаблонам — удобнее через PHP.
Можно ли оставлять unsafe-inline?
Иногда как временный компромисс — да. Но в долгосрочной перспективе лучше переходить на nonce или hash, потому что это заметно усиливает защиту.
Нужно ли использовать report-uri?
Для совместимости это всё ещё практично, особенно на существующих проектах. Но саму систему отчётов лучше строить аккуратно и держать в голове, что браузерная поддержка механизмов репортинга со временем меняется.
Следующие страницы вас также могут заинтересовать:
- Робототехника
- SEO-продвижение образовательного сайта: 7 лет — от старого HTML до AI Search
- Аутсорсинг: что это такое и как он может помочь вашему бизнесу?
- Паллетирование: что это такое, технологии, оборудование и роботизация
- Промышленные роботы: что это, виды, устройство и применение
Обновлено: 25.03.2026