Security Headers в 2026 году — это уже не «галочка для галочки», а нормальная часть технической безопасности сайта. Я регулярно вижу, как у проектов на Bitrix, WordPress и Laravel либо вообще нет заголовков безопасности, либо они настроены криво: сайт работает, но браузер молча оставляет лишние риски открытыми. И это плохая идея.
Что такое Security Headers и зачем они нужны
Security Headers — это HTTP-заголовки, которые браузер получает вместе с ответом сервера и использует, чтобы понять: что можно загружать, откуда можно исполнять скрипты, как обращаться с cookies, разрешать ли встраивание сайта в iframe и так далее. Грубо говоря, это набор правил для браузера, который снижает вероятность XSS, clickjacking, утечек данных и некоторых видов подмены контента.
На моей практике чаще всего недооценивают не сам факт наличия заголовков, а их влияние на реальные инциденты. У одного клиента на WordPress с PHP 8.2 и nginx сайт долгое время жил без CSP, без HSTS и без нормального X-Frame-Options. Вроде бы ничего не ломалось, но после подключения стороннего виджета чата и пары аналитик начались странные подгрузки, а потом кто-то попытался встроить сайт в iframe для фишинговой страницы. Если бы базовые заголовки были включены заранее, часть проблем просто не возникла бы.
Если у вас сейчас в приоритете не только безопасность, но и общая доработка сайта, я обычно советую смотреть на это в комплексе: исправление технического долга, настройка заголовков, проверка SSL, аудит редиректов и контроль форм. В этом смысле часто помогает доработка сайта в формате точечных задач, а не бесконечного «потом сделаем».
И ещё момент. В 2026 году браузеры стали строже, а сторонних интеграций стало больше: платежи, CRM, карты, веб-пуши, аналитика, AI-виджеты. Поэтому настроить заголовки «по старой памяти» уже нельзя. Нужен нормальный, осознанный подход, иначе можно сломать сайт или, что хуже, оставить его с ложным ощущением защищённости.
Какие заголовки нужны в 2026 году
Я обычно начинаю не с кода, а с перечня заголовков, которые реально нужны на сайте. Не надо включать всё подряд. Честно говоря, бессмысленно ставить 15 заголовков, если половина из них конфликтует с вашей CMS, CDN или рекламными скриптами.
Базовый набор на 2026 год выглядит так:
- Content-Security-Policy — ограничивает источники скриптов, стилей, изображений, фреймов и прочего контента.
- Strict-Transport-Security — включает HSTS и принудительно держит сайт на HTTPS.
- X-Frame-Options — защита от встраивания в iframe и clickjacking.
- X-Content-Type-Options: nosniff — запрещает браузеру угадывать тип контента.
- Referrer-Policy — контролирует, что отправляется в Referer.
- Permissions-Policy — ограничивает доступ к камере, микрофону, геолокации, fullscreen и т. д.
- Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy, Cross-Origin-Embedder-Policy — для более строгой изоляции, если проект это позволяет.
На деле я почти всегда начинаю с пяти вещей: CSP, HSTS, X-Frame-Options, nosniff и Referrer-Policy. Permissions-Policy добавляю после проверки реальных сценариев. А вот COOP/COEP включаю только если понимаю, что это не сломает интеграции, iframe, модули оплаты и сторонние сервисы.
Если вам нужен не только список, но и проверка текущего состояния сайта, я обычно советую сначала сделать проверку сайта. Это помогает увидеть, какие заголовки уже есть, где CDN их затирает, а где nginx или Apache вообще не отдают нужные директивы.
Кстати, если у вас уже настроен HSTS, не забывайте, что это необратимая штука в глазах браузера. Добавить легко, а вот «откатить и забыть» не получится моментально. Поэтому на боевом домене я всегда проверяю сначала staging-среду, потом тестовый поддомен, и только потом прод.
Как настроить Security Headers на nginx
На nginx настраивать заголовки обычно удобнее всего. Я это делаю через блок server или через отдельный include-файл, если проект большой и у него несколько виртуальных хостов. На сайтах на Laravel и на WordPress это часто самый чистый путь. В Bitrix тоже нормально работает, если не мешает панель, ajax и старые компоненты.
Ниже пример базовой конфигурации. Это не «идеальный универсальный шаблон», а рабочая отправная точка, которую потом надо адаптировать под ваш сайт.
server {
listen 443 ssl http2;
server_name example.ru www.example.ru;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" 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 Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://www.google-analytics.com https://www.googletagmanager.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" always;
root /var/www/example.ru/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
Да, здесь есть 'unsafe-inline'. И нет, это не потому, что я люблю плохие практики. Это потому, что на реальных проектах, особенно на WordPress и старом Bitrix, без этого часто ломаются инлайновые стили и скрипты. И вот тут уже начинается нормальная работа: постепенно убирать инлайновый код, выносить JS в отдельные файлы, вводить nonce или hash-значения, а не пытаться «сделать идеально» за один вечер.
Если сайт работает за Cloudflare, Nginx Proxy Manager или другим reverse proxy, надо проверять, не перезаписываются ли заголовки на уровне CDN. У меня был случай, когда на сервере всё было настроено правильно, а Cloudflare Page Rules снимали часть заголовков на динамических страницах. Снаружи казалось, что проблема в nginx, хотя виноват был вовсе не он.
И ещё: если у вас несколько приложений на одном сервере, не копируйте один и тот же CSP без проверки. Для Laravel 11 на PHP 8.3 одна политика, для WordPress с Elementor — другая, для Bitrix со встроенным композитом — третья. Иначе вы словите массу ложных блокировок. В таких задачах часто нужна настройка силами специалиста, потому что цена ошибки тут обычно выше, чем стоимость аккуратной реализации.
Установка заголовков в .htaccess и PHP
Если у вас Apache, LiteSpeed или хостинг, где nginx недоступен, часто приходится использовать .htaccess. Для WordPress это вообще классический сценарий. Но здесь надо быть аккуратным: некоторые заголовки могут конфликтовать с кэшем, плагинами безопасности и внешними сервисами.
# .htaccess
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()"
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"
</IfModule>
Но если проект работает на PHP-FPM и вы не хотите лезть в конфиги сервера, часть заголовков можно отдавать из PHP. Я так делаю на небольших проектах, тестовых стендах и в случаях, когда доступ только к коду. На боевых проектах это не лучший вариант, но иногда другого пути нет.
<?php
header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()");
// В бою CSP лучше настраивать очень аккуратно
header("Content-Security-Policy: default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; base-uri 'self'; frame-ancestors 'self';");
?>
Я обычно не советую выносить всё в PHP, если есть возможность сделать это на сервере. Почему? Потому что заголовки должны быть единообразными для статических файлов, ошибок 404/500, картинок, CSS, JS и HTML. И вот тут PHP уже не помогает. Серверный уровень надёжнее.
На WordPress с PHP 8.1 и 8.2 я чаще всего настраиваю заголовки через nginx или через плагин безопасности только как временное решение. На Bitrix — через конфиги веб-сервера и иногда через локальные правила в окружении. На Laravel — через middleware, но опять же после проверки всех внешних подключений. Если проект живёт на PHP 8.4, как в этой статье про настройку PHP 8.4, то заголовки всё равно нужно тестировать отдельно, потому что версия PHP сама по себе безопасность браузера не закрывает.
Особенности Content Security Policy в 2026 году
CSP — самый важный и самый капризный заголовок из всей группы. Именно он чаще всего ломает сайт при неграмотной настройке. Я это вижу постоянно: дизайнеры добавили шрифт с Google Fonts, маркетолог вставил пиксель, разработчик подключил карту, а потом кто-то включил строгую политику и удивился, почему половина интерфейса перестала работать.
По опыту, правильнее идти так: сначала собрать список всех доменов, с которых реально грузится контент. Потом проверить HTML, шаблоны, виджеты, iframe, формы, аналитку, CDN, видео, карты, шрифты, API-запросы. И только после этого писать политику. Иначе будет боль.
В 2026 году я часто вижу следующие источники, которые надо учитывать в CSP:
- Google Tag Manager и Google Analytics;
- Яндекс.Метрика и Вебвизор;
- Cloudflare, jsDelivr, unpkg, cdnjs;
- Stripe, PayPal, YooKassa, CloudPayments;
- Google Fonts и локальные шрифты;
- карты и embed-контент;
- CRM-виджеты и чаты;
- скрипты антибот-защиты и Turnstile.
Если у вас есть сторонние скрипты, которые внедряют inline JS, не надо сразу паниковать. Иногда достаточно перейти на nonce. Иногда — вынести код в отдельный файл. Но если сайт на старом шаблоне и весь собран из инлайна, тогда нужна последовательная переделка. Это уже не про «подкрутить заголовок», а про нормальную доработку сайта и наведение порядка в фронтенде.
Я всегда отдельно смотрю на frame-ancestors. Сейчас это предпочтительнее, чем просто полагаться на X-Frame-Options, потому что CSP даёт более гибкий контроль. Но X-Frame-Options я обычно тоже оставляю, хотя бы как дополнительную страховку для старых браузеров и старых корпоративных сред.
Если говорить совсем по делу, то лучшая практика в 2026 году — это не «сразу жёсткий CSP», а внедрение через report-only. Это особенно полезно, если проект большой. Например, на интернет-магазине с 5000+ товарных страниц, CRM, коллтрекингом и мультирегиональностью вы просто не сможете вручную угадать все источники с первого раза. Тут нужна методичная проверка, а иногда и отдельный аудит, как в материале про Content Security Policy.
HSTS, Referrer-Policy, XFO и Permissions-Policy
Эти заголовки часто воспринимают как «второстепенные», хотя на практике они закрывают очень полезные сценарии. HSTS заставляет браузер ходить только по HTTPS. Referrer-Policy контролирует, сколько информации уходит внешним ресурсам. X-Frame-Options защищает от встраивания в чужой iframe. Permissions-Policy ограничивает доступ к опасным возможностям браузера.
Для HSTS я обычно ставлю срок не меньше года. На боевом сайте с нормальным SSL это адекватно. Если SSL уже настроен и проблем с HTTPS нет, можно включать includeSubDomains, но только если все поддомены тоже на HTTPS. Иначе вы сами себе создадите проблемы. Это особенно актуально для крупных проектов с отдельными поддоменами под mail, admin, cdn, api, staging.
Если вам нужно сначала выстроить HTTPS-часть, а потом уже браться за заголовки безопасности, я бы начал с материалов вроде настройки HTTPS редиректов и HSTS и автопродления SSL-сертификата. Без этого весь разговор о security headers немного теряет смысл.
Referrer-Policy я чаще всего ставлю в значение strict-origin-when-cross-origin. Это разумный баланс. Слишком жёсткое поведение иногда мешает аналитике и внешним интеграциям, слишком мягкое — лишний раз раскрывает URL и параметры запроса. Для большинства сайтов это нормальный вариант.
Permissions-Policy тоже стоит настроить без фанатизма. Не надо выключать всё подряд, если на сайте есть карта, видеочат или камера для загрузки документов. Сначала смотрим сценарии, потом ограничиваем лишнее. И да, это лучше делать на этапе доработки, а не после запуска рекламной кампании.
Как проверить работу заголовков
Проверка — это та часть, которую многие пропускают. А зря. Я видел сайты, где заголовки были прописаны в конфиге, но на проде они не работали из-за кеша, старого reverse proxy или конфликта между nginx и Apache. Поэтому после настройки я всегда делаю проверку с нескольких сторон.
Самый простой способ — открыть DevTools в браузере и посмотреть ответ сервера в Network. Но этого мало. Я обычно использую еще несколько инструментов: curl, Security Headers, отчёты CSP и ручную проверку в разных браузерах. Chrome, Firefox, Safari и мобильные браузеры ведут себя не совсем одинаково. Особенно это заметно, когда речь идёт о iframe, third-party cookies и политике CSP.
curl -I https://example.ru
# или точечно проверить заголовки
curl -I https://example.ru | grep -Ei "strict-transport-security|content-security-policy|x-frame-options|x-content-type-options|referrer-policy|permissions-policy"
Если заголовки есть на HTML, но нет на картинках, JS или ответах 404/500, значит настройка сделана не до конца. Я это очень часто встречаю на сайтах, где заголовки повесили только на один location в nginx или только на основной контроллер в PHP. А безопасность должна работать везде.
И ещё одна штука. Проверяйте не только главную страницу, но и внутренние, карточки товаров, страницы с формами, PDF, картинки, ajax-эндпоинты и ошибки 404/500. В этом смысле полезно сочетать настройку заголовков с мониторингом логов сайта и с критическими уведомлениями о сбоях. Тогда вы быстрее увидите, если что-то пошло не так.
Типичные ошибки и как их исправить
Самая частая ошибка — включить CSP, не зная, что именно загружается на сайте. Вторая по популярности — добавить HSTS на домен, у которого ещё не до конца настроены поддомены. Третья — забыть про CDN, прокси и кеш. Четвёртая — настроить заголовки на проде, но не проверить 301/302 редиректы и ответы ошибок.
Был случай: у клиента на WordPress всё настроили, но на страницах 404 заголовки не отдавались, потому что шаблон ошибки обслуживался отдельным location-блоком. В результате Security Headers работали только на «живых» страницах, а как раз на ошибках, где иногда тоже показываются сторонние скрипты, защита отсутствовала. Мы это исправили, и после этого конфигурация стала стабильной.
Ещё один частый косяк — «сломали оплату». Обычно это не проблема самого заголовка, а проблема того, что не учли домен платёжного шлюза или iframe. Именно поэтому я советую не выкатывать такую настройку без staging-среды. Если у вас её нет, я бы серьёзно подумал о том, чтобы настроить staging-сайт. Это банально дешевле, чем потом чинить прод и терять заявки.
Вот короткий список ошибок, которые я вижу чаще всего:
- используют слишком строгий CSP без анализа внешних ресурсов;
- включают HSTS до полной проверки HTTPS на всех поддоменах;
- забывают про статические файлы и ответы 404/500;
- не проверяют работу сайта в Safari и мобильных браузерах;
- настраивают заголовки в PHP, хотя правильнее делать это на уровне nginx/Apache;
- не учитывают CDN, reverse proxy и кеширование.
Если у вас уже есть технический долг, заголовки безопасности сами по себе его не уберут. Они только уменьшают риск. А чтобы закрыть вопрос системно, часто нужна комплексная работа: защита форм, 2FA для админки, проверка уязвимых плагинов, ограничение доступа по IP, настройка WAF. И да, иногда без нормальной поддержки сайта это всё начинает расползаться обратно.
Практический план внедрения в 2026 году
Если не хочется утонуть в деталях, я бы шёл по такому плану. Сначала делаем инвентаризацию внешних ресурсов: шрифты, скрипты, аналитика, чаты, карты, виджеты, CDN, платежи. Потом включаем базовые заголовки на staging. После этого проверяем сайт в браузере и через curl. Затем запускаем CSP в режиме report-only. И только потом переводим в боевой режим.
Для небольшого сайта на WordPress или Bitrix такой путь обычно занимает от нескольких часов до одного рабочего дня. Для крупного интернет-магазина с десятками интеграций — может уйти 2–5 дней. Это нормально. Иначе потом вы потратите неделю на поиск того, почему не отправляется форма, не открывается модалка или сломался скрипт аналитики.
Если у вас Laravel-проект, я обычно рекомендую вынести часть логики в middleware, а политику CSP хранить отдельно и версионировать. Для Bitrix и WordPress — использовать серверные заголовки и отдельный файл конфигурации, который не правится вручную «на скорую руку». Грубо говоря, всё, что можно автоматизировать и зафиксировать, надо фиксировать.
И последнее. Не забывайте, что Security Headers — это не изолированная тема, а часть общей защиты сайта. Они хорошо сочетаются с HSTS, SSL, 2FA, защитой от брутфорса, мониторингом ошибок и логов, а также с нормальной архитектурой кэша. Если у вас уже настроен сайт по базовым правилам безопасности, заголовки просто усиливают результат. Если же сайт дырявый в целом, заголовки не спасут. Тут уже нужна системная работа и, честно говоря, иногда полноценная доработка сайта под задачу.
На моей практике именно аккуратное, поэтапное внедрение Security Headers даёт лучший результат. Без героизма. Без магии. С проверкой, тестами и пониманием, что и зачем вы включаете. В 2026 году это уже не опция для перфекционистов, а обычная часть здравой веб-разработки.
Готовы усилить защиту сайта заголовками?
Настройте security headers правильно, чтобы снизить риски атак и повысить безопасность сайта уже сегодня.
