Top.Mail.Ru
OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

HTTP Security Headers на практике: CSP, Permissions‑Policy, Referrer‑Policy и X‑Frame‑Options в Nginx и Apache

Покажу, как включить и отладить ключевые HTTP security headers — CSP, Permissions‑Policy, Referrer‑Policy и X‑Frame‑Options — в Nginx и Apache. Дам безопасные пресеты для CMS/SPA/статики, миграцию через Report‑Only и советы по тестированию.
HTTP Security Headers на практике: CSP, Permissions‑Policy, Referrer‑Policy и X‑Frame‑Options в Nginx и Apache

HTTP security headers — простой и эффективный слой защиты, который часто остаётся «на потом». Пара корректно выставленных заголовков может отрезать целые классы атак: XSS через запрет инлайна, кликджекинг через запрет встраивания, утечки referrer-данных и злоупотребления чувствительными API браузера. В этой статье собрал практические пресеты и типовые сценарии для Nginx и Apache, чтобы вы могли внедрить заголовки быстро и без сюрпризов в проде.

Что именно дают эти заголовки

В статье сосредоточимся на четырёх ключевых заголовках безопасности и их роли:

  • Content-Security-Policy (CSP) — ограничивает откуда можно загружать скрипты, стили, изображения, шрифты, а также управляет встраиваемыми фреймами и смешанным контентом. Главный антагонист XSS и произвольных внешних подключений.
  • Permissions-Policy — контролирует доступ страницы и её фреймов к возможностям браузера: геолокации, камере, микрофону, акселерометру, полноэкранному режиму и др.
  • Referrer-Policy — что именно утекает в заголовке Referer при переходах на другие домены. Важен для приватности пользователей и скрытия структуры URL.
  • X-Frame-Options — старый, но ещё полезный способ защититься от встраивания страницы в чужой сайт (кликджекинг). Сегодня дублируется и тоньше настраивается директивой frame-ancestors в CSP.

Эти заголовки — про «умолчания в сторону безопасности». Они задают жёсткие рамки, и если нужно — точечно расширяются. Главное — не наоборот.

Где и как добавлять заголовки: основы для Nginx и Apache

Nginx

Ключевые моменты:

  • Используйте add_header ... always;, чтобы заголовки не исчезали на редиректах и ошибках.
  • Старайтесь задавать заголовки в server-блоке; если у вас разные политики для поддиректорий, используйте location с явными переопределениями.
  • Помните: add_header переопределяется в более узком контексте. Если в location задан add_header, заголовки из server без always могут пропасть.

Apache

Ключевые моменты:

  • Нужен модуль mod_headers. Используйте Header always set для ошибок и внутренних редиректов.
  • Для тонкой настройки на уровне каталогов можно применять .htaccess (учтите накладные расходы), либо конфиг <Directory>/<VirtualHost>. На виртуальном хостинге чаще всего доступен .htaccess, на VDS — полный контроль в конфиге.
  • При наличии обратных прокси следите, чтобы заголовки не дублировались и не конфликтовали с заголовками от апстрима.
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Content‑Security‑Policy (CSP): стратегия и пресеты

CSP — самый мощный и самый «ломающий» заголовок. С него всегда начинаем в режиме отчётов, а уже потом включаем enforcement.

Принципы

  • Базовая философия: «запретить всё, потом разрешить необходимое». Стартуем с default-src 'self' и постепенно расширяем.
  • Минимизируйте использование 'unsafe-inline' и 'unsafe-eval'. Для инлайна применяйте nonce- или sha256-/sha384- хеши.
  • frame-ancestors в CSP заменяет X-Frame-Options и гибче его. Для поддержки старых браузеров оставляйте оба.
  • upgrade-insecure-requests помогает автоматически апгрейдить HTTP-ресурсы до HTTPS и устранить смешанный контент.

Важные директивы

  • default-src — общий «белый список» источников.
  • script-src, style-src, img-src, font-src, connect-src — управление каналами загрузки кода, медиа и сетевых запросов (XHR/Fetch/WebSocket).
  • frame-src и child-src — куда можно встраивать фреймы/воркеры (в актуальных политиках используйте frame-src).
  • frame-ancestors — кто может встраивать вашу страницу как фрейм.
  • object-src — лучше 'none', чтобы отключить устаревшие плагины.
  • base-uri — ограничивает <base>, предотвращая подмену базового URL.
  • form-action — контролирует, куда можно отправлять формы.

Фрагменты конфигурации Nginx и Apache с заголовками безопасности

Как безопасно внедрять CSP

  1. Включите Content-Security-Policy-Report-Only с максимально жёсткими правилами, но без блокировок.
  2. Соберите отчёты о нарушениях и логи браузера (DevTools → вкладки Network/Security/Console).
  3. Поправьте политику, добавив необходимые источники; уберите лишние.
  4. Переключите на боевой Content-Security-Policy. Для критичных зон (админки) можно оставить Report-Only параллельно ещё на некоторое время.

Генерация nonce должна быть на уровне приложения и для каждого ответа. Не используйте предсказуемые значения. Для статических сайтов разумнее переходить на хеши.

Пример строгой CSP для типового сайта

Эта политика подходит как отправная точка для большинства сайтов без внешних CDN и без нужды встраиваться в чужие фреймы:

# Nginx (внутри server):
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; object-src 'none'; upgrade-insecure-requests" always;

# Apache (внутри VirtualHost или .htaccess при включённом mod_headers):
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; object-src 'none'; upgrade-insecure-requests"

Комментарии по пресету:

  • img-src 'self' data: и font-src 'self' data: разрешают встраиваемые data-URL для SVG/favicon/woff2, что часто необходимо.
  • frame-ancestors 'none' запрещает встраивание — хорошо против кликджекинга, но сломает виджеты и предпросмотры в админках, если они требуют фреймов.
  • upgrade-insecure-requests «подтягивает» HTTP-ресурсы к HTTPS, что помогает при миграции. Убедитесь, что на домене установлены корректные SSL-сертификаты. Если вы в процессе переезда на HTTPS и HSTS, пригодится наш разбор: миграция домена, 301, HSTS и SSL.

Добавляем исключения: CMS/WordPress, SPA, внешние CDN

Частые расширения:

  • Для CMS, где инлайн-скрипты/стили не убрать, временно добавляют 'unsafe-inline' к script-src/style-src. Лучше заменить инлайн на файлы или внедрить nonce/hash.
  • SPA и аналитика используют внешние домены в connect-src (API, WebSocket). Их нужно перечислить явно.
  • CDN для статики потребует добавить домены в соответствующие директивы (script-src, style-src, img-src, font-src).
  • Если нужно, чтобы страницу можно было встраивать на ваш поддомен, используйте frame-ancestors 'self' вместо 'none'.

Report‑Only для безопасной миграции

Подготовительный режим без блокировок:

# Nginx:
add_header Content-Security-Policy-Report-Only "default-src 'self'; ..." always;

# Apache:
Header always set Content-Security-Policy-Report-Only "default-src 'self'; ..."

Сбор отчётов можно организовать на собственную эндпоинт-точку приложения. Учитывайте, что report-uri устаревает в пользу report-to, но первый по-прежнему широко поддерживается. Не включайте в отчёты персональные данные.

Permissions‑Policy: закрываем доступ к лишним API

Permissions-Policy пришёл на смену Feature-Policy. Он позволяет включать/выключать возможности браузера для документа и вложенных фреймов. Синтаксис: feature=(...), где в скобках список источников: "self", конкретные источники или пустые () для полного запрета. * разрешает всем.

Базовый пресет «запретить всё лишнее»

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

# Apache:
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=(), accelerometer=(), gyroscope=(), magnetometer=(), usb=(), bluetooth=(), payment=(self), fullscreen=(self)"

Пояснения:

  • Все рискованные датчики отключены. Это снижает поверхность атаки для встраиваемых фреймов третьих сторон.
  • fullscreen и payment оставлены для самого сайта ((self)), что обычно безопасно.
  • Если ваш плеер требует autoplay или сайт — clipboard-read/clipboard-write, добавьте их точечно.

Referrer‑Policy: приватность и аналитика

Рекомендуемое значение по умолчанию — strict-origin-when-cross-origin. Оно отправляет полный URL только в пределах того же происхождения, а наружу — только схему+хост+порт. Часто это золотая середина между приватностью и полезностью для аналитики.

# Nginx:
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Apache:
Header always set Referrer-Policy "strict-origin-when-cross-origin"

Если данные реферера вообще не нужны, используйте no-referrer. Для особо чувствительных разделов (например, страницы с токенами в URL) можно локально ужесточить политику через отдельный location/Directory.

X‑Frame‑Options: наследие, которое ещё работает

X-Frame-Options поддерживается старыми браузерами и полезен как дополнительная страховка к frame-ancestors в CSP. Два практичных значения:

  • DENY — полностью запрещает встраивание.
  • SAMEORIGIN — разрешает встраивание только с того же происхождения.
# Nginx:
add_header X-Frame-Options "DENY" always;

# Apache:
Header always set X-Frame-Options "DENY"

ALLOW-FROM устарел и поддерживается непоследовательно. Для точечного разрешения отдельных источников используйте frame-ancestors в CSP.

Комбинируем всё вместе: минимальный надёжный набор

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

# Nginx (server):
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; object-src 'none'; upgrade-insecure-requests" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), accelerometer=(), gyroscope=(), magnetometer=(), usb=(), bluetooth=(), payment=(self), fullscreen=(self)" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;

# Apache (VirtualHost):
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; object-src 'none'; upgrade-insecure-requests"
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=(), accelerometer=(), gyroscope=(), magnetometer=(), usb=(), bluetooth=(), payment=(self), fullscreen=(self)"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "DENY"

Проверка и отладка

  • Проверьте заголовки через команду: curl -I https://ваш-домен. Убедитесь, что заголовки видны и на 200, и на 301/404 (для Nginx/Apache мы использовали always).
  • Откройте DevTools браузера: вкладка Network → выберите документ → вкладка Headers; вкладка Security подскажет проблемы со смешанным контентом; Console покажет нарушения CSP.
  • Отслеживайте, не дублируются ли заголовки (часто при сочетании CDN/прокси с origin-сервером).

Проверка CSP и заголовков в DevTools браузера

Переключатели по зонам и статике

Нередко политика для админки и публичной части различается. Например, админке нужен фрейм для визуального редактора или предпросмотра. Решение — сделать локальный override:

# Nginx (пример для /admin/ разрешаем фреймы с self):
location /admin/ {
  add_header Content-Security-Policy "default-src 'self'; ...; frame-ancestors 'self'" always;
}

# Apache (Directory):
<Directory "/var/www/site/admin">
  Header always set Content-Security-Policy "default-src 'self'; ...; frame-ancestors 'self'"
</Directory>

Ещё один частый кейс — статика, которую отдаёт другой сервер/поддомен. Добавьте его в img-src/font-src/style-src/script-src, иначе получите блокировки в консоли браузера.

Типовые сценарии и подсказки

Статический сайт или документация

Чаще всего достаточно строгого дефолтного набора. Если генератор статического сайта встраивает инлайн-скрипты для поиска/темизации, рассмотрите переход на хеши. Для примера, разрешите конкретный инлайн-скрипт хешем:

# Фрагмент для CSP со скрипт-хешем (пример SHA-256):
add_header Content-Security-Policy "script-src 'self' 'sha256-...'; style-src 'self'; ..." always;

CMS (WordPress, 1C‑Битрикс, Joomla)

Готовьтесь к множеству инлайн-скриптов/стилей и внешних ресурсов. Мягкий путь миграции — Report-Only + постепенное вырезание инлайна в шаблонах. Если без инлайна никак, внедряйте nonce на уровне приложения и добавляйте script-src 'nonce-...' в CSP. Для административной зоны может понадобиться frame-ancestors 'self'.

SPA/приложение

Особое внимание к connect-src (API и WebSocket), worker-src (если используете Web Worker), а также к frame-src для сторонних виджетов (платёжные шлюзы, карты). Заранее составьте список доменов-провайдеров и пропишите их в соответствующих директивах.

Подводные камни

  • Инлайн в сторонних виджетах. Если вставляете внешний виджет через <iframe>, ваша CSP не контролирует содержимое фрейма, но Permissions-Policy и Referrer-Policy настраивают ограничения для фрейма со стороны контейнера. Для контроля скриптов внутри фрейма потребуется политика на стороне поставщика.
  • Смешанный контент. upgrade-insecure-requests поможет, но если внешний ресурс недоступен по HTTPS, нужно заменить источник или отказаться от ресурса.
  • Кэширование. Промежуточные кэши/CDN могут переопределять или отбрасывать заголовки. Проверьте, что на всех слоях политика одинакова.
  • Дублирование заголовков. Два одинаковых заголовка могут конфликтовать. Держите политику в одном месте: либо на CDN, либо на origin, либо добейтесь идентичности.
  • Совместимость. Старые браузеры игнорируют часть директив CSP и Permissions-Policy. Именно поэтому полезно держать X-Frame-Options параллельно с frame-ancestors, а также включать «усилители» вроде X-Content-Type-Options: nosniff вне темы этой статьи.

Практическая методика внедрения в прод

  1. Инвентаризация ресурсов. Соберите список доменов для скриптов, стилей, шрифтов, изображений, соединений. Зафиксируйте особые места: фреймы, платежные страницы, загрузчики файлов.
  2. Включите Report‑Only. Поставьте жёсткую политику CSP в Report-Only, разгрузите логи по типам нарушений.
  3. Итерации. Разрешайте необходимые источники точечно. По возможности убирайте инлайн в кодовую базу.
  4. Включение Enforcement. Переведите на боевую CSP, оставив Report-Only рядом на неделю‑две для мониторинга хвостов.
  5. Разделение зон. Там, где нужно, задайте локальные ослабления (админка, сервисные пути). Лучше ослаблять локально, а не глобально.
  6. Автоматические тесты. Добавьте проверки заголовков в CI/CD: curl -I по ключевым URL и проверка значений через скрипты. Если поднимаете новый сервер, посмотрите сравнение панелей для VDS.

FAQ по типовым сбоям

  • «Сломались карты/видео/виджет оплаты». Добавьте источник провайдера в frame-src и при необходимости разрешите соответствующие connect-src/script-src. Проверьте Permissions-Policy, возможно требуется fullscreen или payment.
  • «Инлайн-скрипты заблокированы». Временно можно 'unsafe-inline', но лучше внедрить nonce или хеши и поэтапно убрать инлайн.
  • «Referrer стал пустой и поломалась аналитика». Убедитесь, что выбрали подходящую политику. strict-origin-when-cross-origin обычно достаточно информативна и безопасна.
  • «Страница не открывается во фрейме партнёра». Проверьте frame-ancestors и X-Frame-Options. Возможно, нужно ослабить политику для конкретного роута.

Итоги

Грамотно выставленные security headers — это быстрый апгрейд безопасности, заметный для сканеров и реальных атак. Начните с консервативного базового пресета: CSP с default-src 'self', запрет встраивания, строгую политику referrer и жёсткий Permissions-Policy. Затем постепенно открывайте ровно то, что действительно нужно вашему приложению. Двигайтесь итеративно и проверяйте поведение в проде: так защита не ударит по функциональности.

Поделиться статьей

Вам будет интересно

Сроки домена без паники: auto‑renew, Grace/Redemption Period и безопасное продление OpenAI Статья написана AI Fastfox

Сроки домена без паники: auto‑renew, Grace/Redemption Period и безопасное продление

Разбираем жизненный цикл домена: что происходит у регистратора и в реестре после expiration, как работают auto‑renew, grace и rede ...
Cron и фоновые задачи: crontab на хостинге и systemd‑таймеры на VDS, логирование и алерты OpenAI Статья написана AI Fastfox

Cron и фоновые задачи: crontab на хостинге и systemd‑таймеры на VDS, логирование и алерты

Разбираем устойчивые схемы запуска фоновых задач: cron и crontab на виртуальном хостинге, systemd timers на VDS. Готовые шаблоны д ...
Большие загрузки и выдача файлов: client_max_body_size, upload_max_filesize, X‑Accel‑Redirect OpenAI Статья написана AI Fastfox

Большие загрузки и выдача файлов: client_max_body_size, upload_max_filesize, X‑Accel‑Redirect

413 Request Entity Too Large, пустой $_FILES, обрывы по таймаутам и «тяжёлая» раздача через PHP. Разбираем Nginx+PHP: лимиты и тай ...