Главная / База знаний / HTTP-заголовки безопасности: CSP, HSTS, X-Frame-Options
инструкция · 9 октября 2026

HTTP-заголовки безопасности: CSP, HSTS, X-Frame-Options

Разбираем, какие HTTP-заголовки реально защищают сайт от XSS, кликджекинга и подмены протокола, как добавить их в конфигурацию Nginx и как проверить, что они действительно применяются.

Зачем нужны заголовки безопасности, если сайт уже работает по HTTPS

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

HTTP-заголовки безопасности — это набор инструкций, которые сервер передаёт браузеру вместе с обычным ответом, и которые говорят браузеру, как именно вести себя с полученной страницей: каким источникам скриптов и стилей доверять, разрешено ли встраивать страницу в чужой фрейм, стоит ли переходить на HTTPS принудительно даже если пользователь ввёл адрес без протокола. Браузер сам обеспечивает эту защиту на основе полученных инструкций — сервер не делает ничего дополнительного вычислительно, кроме отправки нескольких дополнительных строк в ответе.

Ценность этих заголовков в том, что они не требуют переписывания кода сайта или CMS — это конфигурация на уровне веб-сервера, применимая централизованно ко всем страницам сразу. При этом эффект от них ощутим именно в сценарии реальной атаки — типичный посетитель никогда не заметит их присутствия, но при попытке эксплуатации XSS-уязвимости или кликджекинга разница между их наличием и отсутствием может определить, состоится атака или нет.

Content-Security-Policy: главный барьер против XSS

Content-Security-Policy (CSP) — самый мощный и одновременно самый сложный в настройке из всех заголовков безопасности. Он явно перечисляет, из каких источников браузеру разрешено загружать скрипты, стили, изображения, шрифты и другие ресурсы, и по умолчанию блокирует инлайновые скрипты, встроенные прямо в HTML, — именно инлайновые скрипты чаще всего используются при эксплуатации межсайтового скриптинга (XSS), когда злоумышленник добавляет вредоносный код через уязвимую форму или комментарий.

Пример базовой политики CSP для типичного сайта на CMS с внешним CDN для скриптов
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always;

Директива default-src задаёт правило по умолчанию для всех типов ресурсов, а более специфичные директивы вроде script-src или img-src переопределяют его для конкретного типа. Значение 'self' разрешает загрузку только с того же домена, что и сама страница; конкретные внешние домены (CDN, платёжные виджеты, аналитика) нужно перечислить явно — CSP работает по принципу белого списка, и всё, что не разрешено явно, браузер заблокирует.

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

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

Strict-Transport-Security: запрет на откат к незашифрованному HTTP

Даже если сайт настроен на редирект с HTTP на HTTPS, первый запрос браузера при вводе адреса без протокола всё равно на короткое мгновение уходит по незашифрованному HTTP, прежде чем сервер успевает ответить редиректом, — и именно на этом окне строится атака вида SSL stripping, когда злоумышленник, находясь между пользователем и сервером (например, в открытой Wi-Fi сети), перехватывает этот первый запрос и не даёт браузеру перейти на HTTPS вообще.

HSTS с сроком действия год, применение ко всем поддоменам и заявкой на предзагрузку в браузеры
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Заголовок Strict-Transport-Security (HSTS) решает эту проблему на уровне самого браузера: получив этот заголовок один раз, браузер запоминает, что для этого домена на протяжении срока max-age (в примере — один год в секундах) нужно всегда обращаться напрямую по HTTPS, даже если пользователь явно ввёл адрес с http:// или перешёл по старой ссылке без протокола, — без единого промежуточного незащищённого запроса.

Параметр preload — заявка на включение домена в специальный список, зашитый прямо в код браузеров Chrome, Firefox и других, который проверяется ещё до первого обращения к сайту вообще, устраняя даже то самое первое окно уязвимости. Важная и необратимая на практике оговорка: включение домена в этот список — решение на годы вперёд, поскольку список распространяется с обновлениями браузеров по всему миру, и обратное исключение домена из preload-списка занимает месяцы и не гарантирует мгновенного эффекта на уже установленных у пользователей браузерах.

Именно поэтому включать preload стоит только тогда, когда сайт точно и на длительный срок остаётся исключительно на HTTPS без исключений — если существует хоть какой-то поддомен или сценарий, где HTTPS пока не настроен, preload может неожиданно сделать его полностью недоступным для части посетителей.

X-Frame-Options и frame-ancestors: защита от кликджекинга

Кликджекинг (clickjacking) — атака, при которой злоумышленник встраивает ваш сайт в невидимый iframe поверх собственной страницы с приманкой, так что пользователь, кликая по видимой кнопке чужого сайта, на самом деле кликает по невидимому элементу вашего сайта под ней — например, по кнопке подтверждения перевода средств или изменения настроек аккаунта, о существовании которой пользователь даже не подозревает.

Запрет встраивания страницы в iframe чужими сайтами двумя дополняющими друг друга способами
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self';" always;

X-Frame-Options со значением SAMEORIGIN разрешает встраивание сайта во фрейм только страницами того же домена, полностью запрещая это для любых внешних сайтов. Более современная директива frame-ancestors внутри CSP делает то же самое, но гибче — можно явно перечислить конкретные доверенные домены, которым разрешено встраивать сайт в свой фрейм, что полезно, например, для встраивания собственного виджета на партнёрских сайтах.

На практике стоит указывать оба заголовка одновременно: frame-ancestors — более новый и гибкий стандарт, но X-Frame-Options по-прежнему поддерживается более широким кругом старых браузеров и клиентов, где поддержка соответствующей директивы CSP может отсутствовать или работать иначе. Указание обоих заголовков не создаёт конфликта — современный браузер просто применит более специфичную директиву CSP, а старый воспользуется классическим заголовком.

X-Content-Type-Options: запрет угадывать тип содержимого

Некоторые старые браузеры при получении файла пытаются самостоятельно угадать его реальный тип по содержимому, даже если сервер явно указал другой Content-Type в заголовке ответа — эта функция называется MIME sniffing и изначально задумывалась как удобство для случаев, когда сервер настроен неверно. Проблема в том, что это открывает возможность для атаки: если злоумышленнику удаётся загрузить файл, замаскированный под безобидное изображение, но содержащий исполняемый JavaScript-код, браузер при угадывании типа может решить исполнить его как скрипт.

Запрет браузеру угадывать тип содержимого вопреки заголовку Content-Type сервера
add_header X-Content-Type-Options "nosniff" always;

Заголовок X-Content-Type-Options со значением nosniff прямо запрещает браузеру это поведение — файл интерпретируется строго в соответствии с тем Content-Type, который явно указал сервер, и ни в каком другом качестве. Это один из самых простых заголовков для внедрения: он практически никогда не ломает легитимную функциональность сайта, поскольку требует лишь того, чтобы сервер сам верно и последовательно указывал типы отдаваемых файлов, что для правильно настроенного Nginx выполняется автоматически.

Referrer-Policy: что сайт сообщает о том, откуда пришёл посетитель

При переходе по ссылке с одной страницы на другую браузер по умолчанию передаёт целевому сайту заголовок Referer с полным адресом страницы, с которой был совершён переход — включая иногда чувствительные данные в самом URL, например параметры поиска, токены сессии в устаревших схемах авторизации или внутренние пути административных разделов.

Разумный по умолчанию баланс между полезностью для аналитики и утечкой данных в URL
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Значение strict-origin-when-cross-origin — практичный компромисс для большинства сайтов: при переходе внутри одного и того же домена передаётся полный адрес страницы (что полезно для внутренней аналитики), при переходе на другой домен передаётся только сам домен без пути и параметров запроса, а при переходе с HTTPS на менее защищённый HTTP заголовок Referer не передаётся вовсе, чтобы не раскрывать данные протоколу без шифрования.

Более строгие значения вроде no-referrer полностью убирают этот заголовок при любом переходе, что максимально защищает приватность посетителей, но одновременно лишает сайты-получатели трафика информации об источнике перехода — это стоит учитывать, если для бизнеса важна атрибуция источников трафика во внешней аналитике партнёрских программ или рекламных кабинетов.

Как добавить все заголовки разом в конфигурации Nginx

На практике удобнее не разбрасывать директивы add_header по разным блокам location, а собрать их в одном месте — на уровне server-блока сайта, откуда они автоматически наследуются во всех вложенных location, если явно не переопределены. Ниже — сводный пример типичного набора заголовков для сайта на PHP-CMS с HTTPS.

Сводный набор заголовков безопасности на уровне server-блока Nginx
server {
    listen 443 ssl http2;
    server_name example.ru;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    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;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;
}

Обратите внимание на параметр always в конце каждой директивы add_header — без него Nginx по умолчанию добавляет заголовок только к ответам с кодом 200, 201, 204, 206, 301, 302, 303, 304 или 307, и заголовок молча пропадает из ответов с другими кодами, например 404 или 500, что может остаться незамеченным при поверхностной проверке главной страницы, но проявится при аудите безопасности всего сайта.

Ещё одна практическая деталь: если один и тот же заголовок добавлен и в основном server-блоке, и в конкретном location, Nginx использует только более специфичное значение из location, полностью игнорируя внешнее — директивы add_header не суммируются и не наследуются частично, поэтому при переопределении одного заголовка в конкретном блоке нужно продублировать туда и остальные, если они там тоже нужны.

Проверка: не верьте конфигурации на слово

Сохранённый и даже перезагруженный конфигурационный файл Nginx не гарантирует, что заголовки реально доходят до браузера — опечатка в синтаксисе, конфликт с другим блоком конфигурации или кэширующий прокси между сервером и пользователем способны молча исказить или вовсе убрать ожидаемый заголовок из финального ответа.

Быстрая проверка всех заголовков ответа сервера без загрузки браузера
curl -I https://example.ru

Команда curl -I отправляет HEAD-запрос и показывает полный набор заголовков ответа сервера — этого достаточно, чтобы за секунды убедиться, что каждый из настроенных заголовков безопасности действительно присутствует в ответе именно с тем значением, которое ожидалось, а не осталось только в конфигурационном файле. Проверять стоит не только главную страницу, но и хотя бы одну внутреннюю страницу и страницу с ошибкой 404 — заголовки, забытые в отдельном location или зависящие от кода ответа, на главной странице могут выглядеть настроенными правильно, а на остальных отсутствовать.

Дополнительно полезно открыть вкладку «Сеть» (Network) в инструментах разработчика браузера и посмотреть заголовки реального запроса страницы — это также покажет, не блокирует ли CSP какие-либо ресурсы страницы прямо сейчас: браузер выводит подробное сообщение в консоли о каждом заблокированном источнике с указанием, какая именно директива CSP сработала, что заметно ускоряет отладку по сравнению с гаданием, какое именно правило политики оказалось слишком строгим.

Типичные ошибки при внедрении заголовков безопасности

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

Включение HSTS preload для домена, который используется не только для готового production-сайта, но и, например, для тестового окружения на поддоменах без HTTPS — includeSubDomains распространяет требование на все поддомены без исключения, и любой из них, работающий пока без сертификата, станет полностью недоступен.

Забытый параметр always в директиве add_header — заголовок применяется только к части ответов сервера, что создаёт ложное ощущение полной защиты при поверхностной проверке главной страницы сайта.

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

Итог: с чего начать, если заголовков ещё нет вообще

Если ни один из этих заголовков ещё не настроен, разумная последовательность внедрения такая: сначала X-Content-Type-Options и X-Frame-Options — они почти никогда ничего не ломают и внедряются буквально за одну строку конфигурации каждый. Следом Strict-Transport-Security с умеренным max-age без preload, пока не появится полная уверенность, что весь сайт и все его поддомены постоянно работают по HTTPS.

Content-Security-Policy стоит внедрять последним и обязательно через режим Report-Only на старте — это единственный из перечисленных заголовков, который требует реального тестирования под конкретный сайт с его сторонними скриптами и виджетами, прежде чем включать в блокирующем режиме. После добавления каждого заголовка проверяйте результат командой curl -I, а не полагайтесь на то, что конфигурация была применена правильно только потому, что Nginx перезагрузился без ошибок.

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

почему мы

Почему стоит выбрать нас

Быстрый запуск

Подберём конфигурацию и развернём проект в день обращения.

Надёжность

NVMe-диски, защита от DDoS и резервные копии по расписанию.

Поддержка 24/7

Дежурные инженеры на связи в любое время суток.

Настройка под ключ

Установим CMS, настроим веб-сервер, SSL и почту за вас.

Перенос без простоя

Аккуратно перенесём сайт и базу данных на новый сервер.

Прозрачные цены

Актуальные тарифы и честный подбор без навязывания.

частые вопросы

Вопросы и ответы

Достаточно ли одних заголовков безопасности, чтобы считать сайт защищённым?

Нет, это один из уровней защиты, а не единственный — заголовки снижают ущерб от определённых классов атак (XSS, кликджекинг, подмена протокола), но не заменяют своевременные обновления CMS и плагинов, безопасный код и защиту сервера в целом.

Может ли неправильно настроенный CSP сломать сайт?

Да, и это самая частая проблема на практике — слишком строгая политика блокирует легитимные скрипты, стили или шрифты, включая сторонние виджеты аналитики и оплаты, если их источники не были явно перечислены в директивах CSP.

Что произойдёт, если добавить домен в HSTS preload список, а потом решить вернуться на HTTP?

Домен останется недоступен по HTTP во всех браузерах, которые используют предзагруженный список Chromium, Firefox и других — исключение из списка возможно, но занимает недели или месяцы и требует времени на распространение обновлённого списка по браузерам.

Нужны ли эти заголовки, если у сайта уже настроен SSL-сертификат?

Да, SSL-сертификат шифрует канал передачи данных, но не защищает от XSS, кликджекинга или подмены типа содержимого — это разные классы угроз, требующие разных мер защиты, и заголовки безопасности дополняют, а не дублируют HTTPS.

Какой заголовок стоит настроить в первую очередь, если нет времени на все?

Strict-Transport-Security и X-Content-Type-Options дают заметный эффект при минимальном риске что-либо сломать; Content-Security-Policy даёт наибольшую защиту от XSS, но требует более внимательной настройки и тестирования перед включением в боевом режиме.

Как проверить заголовки безопасности без специальных инструментов?

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

Работает ли frame-ancestors в старых браузерах, где нет поддержки CSP?

Нет, в этом случае стоит дополнительно указать классический заголовок X-Frame-Options — он поддерживается заметно более широким кругом браузеров и служит запасным вариантом там, где CSP frame-ancestors ещё не поддерживается.

Может ли Referrer-Policy повлиять на аналитику сайта?

Да, слишком строгая политика (например, no-referrer) скрывает от аналитики сайта-получателя, откуда пришёл посетитель, что усложняет анализ источников трафика — для большинства сайтов разумным компромиссом считается strict-origin-when-cross-origin.

Как оплачивать услуги?

Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.

Есть ли круглосуточная поддержка?

Да, дежурные инженеры на связи 24/7.

Выдаёте ли закрывающие документы?

Да, предоставим необходимые документы для оплаты.

Как оставить заявку?

Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.

заявка

Оставьте заявку

Ответим и поможем с подбором и настройкой.