Если страницу сайта можно без ограничений встроить в <frame>, <iframe>, <embed> или <object> на стороннем сайте, это открывает дорогу для атак типа clickjacking. Пользователь видит ваш интерфейс, но поверх него могут быть наложены прозрачные элементы, чужие кнопки, псевдо-формы, обманные действия или сценарии, заставляющие его кликнуть не туда, куда он собирался.
Классическая защита от такого сценария — HTTP-заголовок X-Frame-Options. Но в современных проектах правильнее смотреть на задачу шире: сегодня основным и более гибким механизмом считается Content-Security-Policy: frame-ancestors, а сам X-Frame-Options удобно использовать как дополнительный защитный слой и для совместимости со старыми браузерами.
Коротко: если вы не хотите, чтобы ваш сайт отображался во фрейме на чужих доменах, задайте защиту на уровне HTTP response headers. Не через meta-теги, а именно через заголовки сервера.

От чего реально защищает X-Frame-Options
Основная задача этого заголовка — уменьшить риск clickjacking. Типовая схема атаки выглядит так:
- злоумышленник встраивает ваш сайт или конкретную страницу в iframe на своём домене;
- поверх интерфейса размещает невидимые или маскирующие элементы;
- пользователь кликает по тому, что выглядит как обычный элемент интерфейса;
- на деле выполняется не то действие, которое он ожидал.
Особенно неприятны такие сценарии на страницах входа, подтверждения действий, личных кабинетах, CMS, админках, формах оплаты и любых экранах, где есть значимые действия пользователя.
Что изменилось в современной практике
В старых статьях по веб-безопасности обычно делают упор только на X-Frame-Options. Это по-прежнему рабочий заголовок, но он уже не является самым гибким и современным способом управления встраиванием страницы.
Сегодня для новых проектов лучше ориентироваться на такую логику:
- X-Frame-Options — простой и понятный защитный заголовок;
- CSP frame-ancestors — более современный и гибкий механизм;
- в идеале использовать оба: CSP как основную политику, X-Frame-Options как дополнительную совместимость.
Важно: защита через X-Frame-Options и через Content-Security-Policy: frame-ancestors задаётся только HTTP-заголовками. Попытка настроить X-Frame-Options через <meta http-equiv> не считается корректным и рабочим способом.
Основные значения X-Frame-Options
DENY
Полностью запрещает отображение страницы во фрейме, даже если попытка идёт с того же сайта.
X-Frame-Options: DENY
Это хороший вариант для максимально строгой защиты, если страницу в принципе не нужно встраивать никуда.
SAMEORIGIN
Разрешает отображение только в том случае, если родительская страница имеет тот же origin, что и сама страница.
X-Frame-Options: SAMEORIGIN
Это самый популярный компромиссный вариант: страница может использоваться во фрейме внутри своего же сайта, но не может быть безопасно встроена на стороннем домене.
ALLOW-FROM
Исторически существовал вариант:
X-Frame-Options: ALLOW-FROM https://example.com
Но сегодня такой подход считается устаревшим. Современные браузеры либо не поддерживают его, либо игнорируют. Поэтому если вам нужно разрешить встраивание страницы для конкретных доверенных доменов, лучше использовать CSP frame-ancestors.
Современная альтернатива: CSP frame-ancestors
Если нужно не просто “запретить всё” или “разрешить только same origin”, а гибко описать, кто именно может встраивать страницу, используйте:
Content-Security-Policy: frame-ancestors 'self';
Или более строгий вариант:
Content-Security-Policy: frame-ancestors 'none';
А если нужно разрешить только конкретные доверенные источники:
Content-Security-Policy: frame-ancestors 'self' https://partner.example.com https://cabinet.example.org;
Такой подход значительно удобнее, чем старый ALLOW-FROM, и лучше подходит для реальных современных интеграций.
Что лучше ставить на практике
Для большинства сайтов оптимальная схема выглядит так:
- если страницу нельзя встраивать вообще — ставим
X-Frame-Options: DENYиContent-Security-Policy: frame-ancestors 'none'; - если внутри своего сайта фреймы допустимы — ставим
X-Frame-Options: SAMEORIGINиContent-Security-Policy: frame-ancestors 'self'; - если нужны исключения под доверенные внешние домены — основной контроль делаем через
frame-ancestors
Практическая рекомендация: для новых проектов лучше ориентироваться именно на frame-ancestors, а X-Frame-Options оставлять как дополнительную защиту и совместимость.
Настройка через .htaccess (Apache)
Если вы хотите разрешать фреймы только внутри своего сайта, базовая настройка для Apache может выглядеть так:
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Content-Security-Policy "frame-ancestors 'self';"
</IfModule>
Если нужно полностью запретить встраивание страницы где бы то ни было:
<IfModule mod_headers.c>
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none';"
</IfModule>
Такой вариант подходит для страниц входа, административных разделов, внутренних кабинетов, API-документации и других чувствительных зон.
Настройка в Nginx
Аналогичная защита для Nginx:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self';" always;
Для полного запрета:
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none';" always;
Настройка через PHP
Если вам удобнее управлять заголовками на уровне приложения, то задать их можно прямо в PHP до любого вывода HTML:
<?php
declare(strict_types=1);
header('X-Frame-Options: SAMEORIGIN');
header("Content-Security-Policy: frame-ancestors 'self';");
Или для максимально строгого режима:
<?php
declare(strict_types=1);
header('X-Frame-Options: DENY');
header("Content-Security-Policy: frame-ancestors 'none';");
Почему meta-тег здесь не подходит
В старых примерах иногда можно встретить такой вариант:
<meta http-equiv="X-Frame-Options" content="deny">
Для реальной защиты это неправильный путь. Браузеры ожидают X-Frame-Options как HTTP response header. Поэтому ставить его нужно на уровне сервера, прокси или приложения, а не в HTML-разметке.
Ошибка: если вы оставили только <meta http-equiv="X-Frame-Options">, у вас может создаваться ложное ощущение, что защита настроена, хотя на практике она не работает так, как ожидается.
Что делать, если нужен Вебвизор Яндекс.Метрики
Здесь начинается самая неприятная часть практики. Исторически некоторые функции Яндекс.Метрики, связанные с воспроизведением и отображением данных во фрейме, могли конфликтовать с жёсткой защитой от framing. Из-за этого в интерфейсе Метрики могли появляться ошибки вида “установлен запрет на отображение страницы во фрейме”.
Но важный момент: это не повод делать глобальную слабую защиту для всего сайта. Хуже всего — строить исключение на основе HTTP_REFERER и считать, что это “правильное решение по безопасности”. Такой путь хрупкий и годится только как legacy-компромисс в старых проектах, где совсем нет других вариантов.
Что лучше делать вместо этого
- сначала решить, действительно ли вам нужно ослаблять защиту ради конкретного инструмента;
- если исключение действительно нужно — делать его как можно уже и точнее;
- по возможности использовать отдельную политику не для всего сайта, а только для нужных страниц;
- если требуется разрешить конкретный внешний trusted origin — делать это через
CSP frame-ancestors, а не через устаревшийALLOW-FROMили общую логику по referer; - все такие послабления сначала тестировать на staging-окружении.
Для большинства сайтов базовая защита должна оставаться строгой, а исключения — быть редкими, точечными и хорошо обоснованными.
Пример точечного ослабления через PHP
Если на проекте действительно есть отдельный участок, которому нужно другое поведение, лучше задавать заголовки условно и явно:
<?php
declare(strict_types=1);
$requestUri = $_SERVER['REQUEST_URI'] ?? '/';
if (strpos($requestUri, '/public-preview/') === 0) {
header('X-Frame-Options: SAMEORIGIN');
header("Content-Security-Policy: frame-ancestors 'self' https://trusted.example.com;");
} else {
header('X-Frame-Options: DENY');
header("Content-Security-Policy: frame-ancestors 'none';");
}
Это намного профессиональнее, чем пытаться принимать решение только по HTTP_REFERER, user-agent или другим косвенным признакам.
Как проверить корректность настройки
После внедрения обязательно проверьте реальные HTTP response headers. Смотреть нужно не HTML-код страницы, а именно заголовки ответа сервера.
Проверить можно так:
- через DevTools в браузере, вкладка Network;
- через
curl -I https://example.com/; - через онлайн-сервисы проверки security headers;
- через собственные smoke-тесты на нужных шаблонах и URL.
curl -I https://example.com/
В ответе вы должны увидеть хотя бы один из заголовков:
X-Frame-Options: SAMEORIGIN
и/или:
Content-Security-Policy: frame-ancestors 'self';
Типовые ошибки при настройке
- надеяться на
<meta http-equiv="X-Frame-Options">вместо серверного заголовка; - использовать
ALLOW-FROMкак будто это современное и поддерживаемое решение; - задавать защиту только на части HTML-страниц, забывая про остальные шаблоны;
- не проверять реальные response headers после внедрения;
- ослаблять защиту глобально ради одного стороннего сервиса;
- строить исключения по
HTTP_REFERERи считать это надёжной моделью безопасности; - не тестировать работу важных виджетов, iframe и аналитики после включения политики.
Что выбрать в реальном проекте
Универсального единственного ответа нет, но для большинства современных сайтов разумная стратегия такая:
- если фреймы не нужны вообще — ставьте
DENY+frame-ancestors 'none'; - если свои фреймы внутри сайта допустимы — ставьте
SAMEORIGIN+frame-ancestors 'self'; - если есть доверенные внешние встраивания — управляйте ими через
frame-ancestorsс явным allowlist; - если есть конфликт с аналитикой или legacy-интеграцией — делайте точечное исключение, а не слабую глобальную политику.
Вывод
X-Frame-Options — по-прежнему полезный защитный заголовок от clickjacking, но в современной практике лучше рассматривать его не изолированно, а вместе с Content-Security-Policy: frame-ancestors.
Если вам нужна просто понятная защита “по умолчанию”, ставьте SAMEORIGIN или DENY. Если нужен современный и гибкий контроль над тем, кто может встраивать страницу, — используйте frame-ancestors. А любые исключения, особенно ради сторонних сервисов и аналитики, делайте узко, осознанно и только после тестирования.
FAQ
Что лучше: X-Frame-Options или CSP frame-ancestors?
Для новых проектов приоритетнее frame-ancestors, потому что это более современный и гибкий механизм. Но X-Frame-Options всё ещё полезен как дополнительный слой и для совместимости.
Работает ли X-Frame-Options через meta-тег?
Нет, на практике защиту нужно задавать через HTTP response header, а не через <meta http-equiv>.
Можно ли использовать ALLOW-FROM?
Как современную рекомендацию — нет. Это устаревший путь. Для allowlist по внешним доменам лучше использовать Content-Security-Policy: frame-ancestors.
Нужно ли убирать защиту ради Вебвизора?
Обычно нет. Сначала нужно понять, действительно ли есть конфликт, и только потом делать точечное исключение. Глобально ослаблять защиту ради одного инструмента — плохая практика.
Какой базовый вариант подходит большинству сайтов?
Для большинства проектов хороший старт — это X-Frame-Options: SAMEORIGIN и Content-Security-Policy: frame-ancestors 'self';.
голосов
Следующие страницы вас также могут заинтересовать:
- Настройка Cloudflare для защиты и оптимизации сайта
- Калькулятор конверсии онлайн | Расчет конверсии сайта
- Робототехника
- SEO умного фильтра Bitrix
- Userator
Обновлено: 25.03.2026