Сайт или виджет не открывается в iframe: X-Frame-Options и frame-ancestors
Страница открывается в отдельной вкладке, но на сайте вместо встроенной карты, календаря или кабинета появляется пустой блок. Иногда браузер пишет refused to connect iframe, иногда сообщает о нарушении Content Security Policy. Это ещё не означает, что поставщик недоступен: документ может отвечать нормально и одновременно запрещать показ внутри чужой страницы.
Если сайт не открывается в iframe, сначала установите источник ограничения. Правила могут принадлежать странице, которую встраивают, или родительскому документу, который пытается её загрузить. Исправление зависит от этой границы. Ниже разберём X-Frame-Options, frame-ancestors, всю цепочку родительских origin и проверку разрешённого встраивания без отключения защиты остальных страниц.
Сначала подтвердите причину блокировки
Откройте инструменты разработчика до воспроизведения проблемы. В Console найдите сообщение, связанное с нужной рамкой, а в Network — загрузку её документа. Зафиксируйте адрес основной страницы, исходный адрес виджета, время и версию браузера. Один скриншот пустого прямоугольника обычно не показывает, какое правило нарушено.
Сообщение с явным упоминанием X-Frame-Options или frame-ancestors указывает на ограничение встраиваемого документа. Упоминание frame-src направляет к политике родителя. Но общая надпись refused to connect может сопровождать другие проблемы: недоступность адреса, ошибку TLS, неподходящую схему или отказ приложения. Не выбирайте исправление только по короткой фразе в рамке.
| Наблюдение | Следующая проверка |
|---|---|
| Console указывает frame-src, загрузка дочернего документа заблокирована | Эффективная директива в политике непосредственного родителя |
| Получен ответ документа, затем указан запрет frame-ancestors | Разрешённые предки в ответе этого документа |
| Виджет отображается, но кнопка не действует | Ошибки кода, ограничения sandbox и последующие запросы |
| Вместо ожидаемого экрана получена страница входа | Конечный адрес, политика этого документа и условия сессии |
Отдельное открытие адреса полезно как сравнение, но не доказывает возможность встроенного показа. Вкладка верхнего уровня не имеет той же цепочки предков. Различаться могут также сессия, адрес после перенаправления и полученное содержимое. Сравнивайте конкретные запросы, а не только впечатление «по ссылке работает».
Для воспроизведения выберите контролируемое действие без платежей и реальных отправок клиентам. Сначала требуется подтвердить загрузку документа и нужного экрана. Если виджет поддерживает специальный режим проверки, используйте его и укажите отличия от рабочего сценария.
Читайте заголовки именно встроенной страницы
Запрет X-Frame-Options и правило frame-ancestors приходят в HTTP-ответе документа, который собираются показывать внутри рамки. Добавить разрешение только в заголовки основного сайта недостаточно. Например, если магазин встраивает кабинет поставщика, правила этого кабинета нужно искать на его ответе, а не на главной странице магазина.
Выберите соответствующий запрос типа document в Network и сохраните значения Content-Security-Policy и X-Frame-Options. Не ограничивайтесь просмотром HTML: нужное правило может формироваться сервером, шлюзом или CDN. Заголовки файла JavaScript тоже не заменяют заголовки загружаемого документа.
Проследите перенаправления. Адрес календаря способен привести на страницу входа, другой домен или страницу ошибки с собственной политикой. В таком случае исследуйте ответ, который браузер действительно пытается отобразить. Разрешение на исходном маршруте не означает разрешения для любого последующего документа.
Статус 200 означает успешный HTTP-ответ по правилам приложения, но не разрешённое встраивание. Проверка командным клиентом помогает увидеть заголовки, однако не воспроизводит решение браузера по всей цепочке предков. Если клиент использует другую авторизацию, он может получить и другой документ с отличающимися правилами.
Сопоставьте внешний ответ с настройками инфраструктуры. Если приложение отправляет одно значение, а прокси добавляет другое, изменение только приложения не завершит работу. Зафиксируйте владельца каждого слоя и точку, где формируется итоговая политика. Проверка должна повторяться через тот же публичный маршрут, которым пользуются посетители.
Что означают DENY и SAMEORIGIN
Справочник MDN по X-Frame-Options описывает два рабочих значения. DENY запрещает показ документа в рамке независимо от происхождения родителя. SAMEORIGIN допускает встраивание, когда все предки имеют тот же origin, что и сам документ. Это ограничение браузерного показа, а не проверка доступности сервера.
Origin определяется схемой, хостом и портом. При одинаковом владельце https://shop.example и https://account.shop.example остаются разными origin. Поэтому SAMEORIGIN не означает «разрешить всем сайтам нашей компании». Совпадение логотипа, общей учётной записи или основной части доменного имени здесь ничего не меняет.
Значение ALLOW-FROM устарело: современные браузеры его не поддерживают как надёжный способ разрешить выбранного партнёра. Не заменяйте им DENY в надежде получить совместимый список исключений. Для согласованного списка источников используют frame-ancestors и отдельно определяют политику поддержки старых клиентов.
X-Frame-Options должен приходить HTTP-заголовком. Его запись через HTML-элемент meta не создаёт действующего разрешения. Установка атрибута с похожим названием на <iframe> также не меняет ответ другого сервера. Исправление выполняют там, где владелец встроенного документа управляет его HTTP-политикой.
Как frame-ancestors задаёт разрешённых предков
Директива frame-ancestors в Content-Security-Policy определяет, какие источники могут обрамлять данный документ. Значение 'none' запрещает встраивание, 'self' разрешает соответствующий собственный origin, а явный список позволяет описать согласованные источники партнёров. Правило принадлежит встраиваемой странице.
У frame-ancestors нет наследования от default-src. Политика default-src 'none' сама по себе не задаёт запрет на встраивание этой страницы. Не делайте вывод о её разрешённых родителях по общей строгости остальных директив: нужное условие проверяют отдельно.
Эта директива не поддерживается в CSP, переданной через meta. Нужен HTTP-заголовок ответа. Если разработчик добавил строку в шаблон страницы и объявил настройку законченной, попросите показать фактически полученный заголовок и результат проверки в браузере.
Список разрешений составляйте из необходимых источников, а не из всех адресов, которые встретились при диагностике. Укажите владельца интеграции и причину каждого исключения. Тестовый стенд, временный домен и рабочий портал — разные решения; их не следует автоматически объединять ради удобства выпуска.
Разрешение обрамлять страницу не предоставляет пользователю право на её данные. Проверка сессии и полномочий остаётся в приложении. Поэтому после изменения политики нужно сохранить исходные условия доступа: календарь не должен становиться публичным кабинетом только потому, что его разрешили показывать у партнёра.
Не перепутайте frame-src родителя и frame-ancestors ребёнка
Встраивание требует совместимости правил с обеих сторон. Родитель разрешает загрузить определённый дочерний документ, а тот допускает своё размещение у соответствующих предков. Изменение одной стороны не отменяет ограничение другой.
| Правило | Где искать | Какой вопрос решает |
|---|---|---|
| frame-src | CSP родительского документа | Откуда эта страница может загрузить содержимое рамок |
| frame-ancestors | CSP встраиваемого документа | Какие предки могут показывать эту страницу внутри себя |
| X-Frame-Options | Ответ встраиваемого документа | Допустим ли показ в рамке по правилам этого заголовка |
Согласно описанию frame-src, при её отсутствии браузер использует child-src, а затем default-src. Поэтому отсутствие явной frame-src не всегда означает отсутствие ограничения загрузки. Эта цепочка отличается от поведения frame-ancestors.
В задаче подрядчику называйте документ и директиву вместе: «разрешить загрузку календаря в политике портала» и «разрешить порталу встраивать календарь» — разные изменения. Такая формулировка предотвращает распространённую ошибку, когда нужный домен добавляют на неверной стороне и затем бессистемно расширяют остальные разрешения.
Проверьте всю цепочку родительских origin
Интеграция может быть вложена глубже одной рамки. Например, портал клиента открывает контейнер партнёра, а уже тот загружает календарь. Для конечного документа родителями в широком смысле являются и ближайший контейнер, и верхняя страница. Разрешение только верхнего origin недостаточно.
| Уровень учебной схемы | Документ | Что важно проверить |
|---|---|---|
| Верхняя страница | https://portal.example | Разрешена загрузка контейнера партнёра |
| Промежуточная рамка | https://shell.example | Допустим её показ в портале и загрузка календаря |
| Конечный календарь | https://widget.example | В разрешениях предков учтены portal.example и shell.example |
В этой условной схеме календарь с разрешением только https://portal.example будет заблокирован из-за промежуточного https://shell.example. Если разрешён только контейнер, проблема возникнет на верхнем предке. Проверка должна пройти для каждого уровня, а не для любого одного совпадения.
Перечень источников не задаёт порядок вложенности и не требует, чтобы все перечисленные страницы присутствовали одновременно. Он определяет допустимые origin каждого фактического предка. Поэтому добавление партнёрского источника означает реальное расширение допустимых контекстов и требует согласования.
Уточните, одинаков ли путь у всех пользователей. Конструктор страниц, предпросмотр в административной панели или портал посредника могут добавлять ещё одну рамку. Проверка прямого встраивания на отдельной тестовой странице не подтверждает работоспособность такой дополнительной цепочки.
Учитывайте приоритет CSP и несколько политик
В правилах CSP о взаимодействии с X-Frame-Options указано: если ответ содержит frame-ancestors в применяемой, enforced-политике, X-Frame-Options игнорируется соответствующей современной моделью обработки. Наличие любого заголовка CSP недостаточно: важны именно директива и режим её применения.
Политика Content-Security-Policy-Report-Only не заменяет блокирующую политику и не отменяет X-Frame-Options. Её диагностическое сообщение также не доказывает фактическую блокировку. В отчёте укажите, какое действующее правило запретило показ, а какое только описало возможное нарушение.
Если встроенный документ получает несколько enforced CSP, их разрешения не складываются в общий список. Каждая применяемая политика должна допускать встраивание. Дополнительный заголовок с разрешённым партнёром не отменит frame-ancestors 'none', который продолжает добавлять другой компонент.
Старый клиент без нужной поддержки CSP может продолжать руководствоваться строгим X-Frame-Options. Поэтому сочетание партнёрского разрешения с SAMEORIGIN способно работать в современном браузере и не работать в устаревшем. Не выдавайте такую конфигурацию за универсально совместимую. Согласуйте поддерживаемые браузеры и отдельный путь для остальных, например официальное открытие документа вне рамки.
Образцы заголовков для конкретных условий
Следующие строки показывают только правила встраивания в HTTP-ответе. Они не заменяют полную CSP сайта и не предполагают удаления других директив. При внедрении изменяют согласованное правило в существующем наборе политик; добавлением второго, более свободного заголовка запрет не ослабить.
Первый вариант предназначен для документа, который разрешено обрамлять только страницами собственного origin. Согласованные ограничения современного CSP и X-Frame-Options выглядят так:
Content-Security-Policy: frame-ancestors 'self';
X-Frame-Options: SAMEORIGIN
Второй вариант относится к конечному календарю из учебной цепочки. В браузере с поддержкой frame-ancestors он допускает только перечисленные origin предков:
Content-Security-Policy: frame-ancestors https://portal.example https://shell.example;
У второго варианта нет эквивалентного списка партнёров в X-Frame-Options. Решение о сохранении строгого XFO для старых клиентов или отказе от него принимают для этого маршрута с учётом требований защиты и поддержки. Не удаляйте заголовок со всего сайта ради одного разрешённого виджета.
На промежуточном контейнере отдельные относящиеся к нему директивы могут выглядеть следующим образом:
Content-Security-Policy: frame-ancestors https://portal.example; frame-src https://widget.example;
Верхнему порталу, в свою очередь, нужно разрешение загрузить https://shell.example в его собственной эффективной директиве frame-src. Эти примеры предполагают обычные HTTPS-документы без дополнительных ограничений sandbox и без конфликтующих политик. У каждого ответа свои правила; переносить строку календаря в ответ портала бессмысленно.
Sandbox, CORS и доступ к DOM решают другие задачи
Атрибут sandbox не обходит запрет X-Frame-Options или frame-ancestors. В документации элемента iframe он описан как набор дополнительных ограничений: например, для выполнения скриптов, отправки форм и происхождения документа. Если рамка уже загрузилась, но действие внутри не выполняется, проверьте эти условия отдельно.
Не удаляйте sandbox целиком по первому сообщению об ошибке. Сопоставьте разрешения с официальной инструкцией виджета и ожидаемыми действиями. Неосторожное сочетание разрешений может лишить ограничение его смысла. Отдельный атрибут allow относится к возможностям браузера и также не переписывает политику встраиваемого сервера.
CORS не служит разрешением показать страницу в iframe. Добавление Access-Control-Allow-Origin не отменяет запрет обрамления. И обратное тоже верно: разрешённое встраивание не даёт родительскому JavaScript произвольно читать DOM документа с другого origin. Эта граница следует из политики одинакового происхождения.
Если родитель должен получать результат выбора внутри виджета, используйте предусмотренный поставщиком интерфейс взаимодействия. Сам факт появления рамки не доказывает, что обмен данными настроен. Для проверки нужны ожидаемое сообщение или другой согласованный результат, а не попытка обойти ограничение прямым чтением чужого документа.
Как согласовать исправление со сторонним сервисом
Если встроенная страница принадлежит партнёру, её заголовки меняет он. Передайте точный URL документа, origin всех предков, сообщение браузера и цель интеграции. Уточните, существует ли официальный embed-адрес: обычная страница кабинета и предназначенный для встраивания виджет могут иметь разные политики.
Не считайте запрет ошибкой автоматически. Владелец сервиса мог намеренно исключить показ кабинета внутри чужого интерфейса. Корректное решение — согласованный режим встраивания, официальный виджет либо открытие разрешённой страницы отдельно. Возможность изменить HTML на своём сайте не даёт права менять ограничения чужого документа.
Учебный разбор: портал раньше напрямую открывал календарь, и его origin был разрешён. После обновления интеграции появилась промежуточная рамка партнёра. Календарь перестал отображаться, хотя адрес и верхний сайт остались прежними. Команда подтверждает дополнительного предка в фактической цепочке и согласует его необходимость с поставщиком.
После точечного изменения политики календаря проверяют разрешение контейнера на собственную загрузку и его frame-src для календаря. Если внутри появляется экран входа, это уже следующий установленный результат. Сессии, сторонние cookie и повторная авторизация требуют отдельной диагностики; их не исправляют расширением frame-ancestors.
В протоколе сохраните первоначальную причину, изменённое правило и проверенную цепочку. Не объявляйте решёнными все случаи пустого виджета, если подтверждён только один путь. Особенно это важно, когда рабочий сайт, мобильное приложение с веб-представлением и административный предпросмотр используют разные контексты.
Проверьте разрешённый показ и сохранение ограничений
Проверку выполняют из той страницы и с той вложенностью, которые будут у посетителя. Одного успешного HTTP-запроса к виджету недостаточно. Учитывайте поддерживаемые браузеры, авторизованный и гостевой варианты, а также перенаправления на другие документы в ходе разрешённого сценария.
- Основная страница получает ожидаемую политику загрузки дочерних документов.
- Встроенный документ разрешает каждый фактический origin предков.
- Внешние ответы не содержат неожиданной дополнительной блокирующей CSP.
- Документ отображается, а нужное безопасное действие завершается.
- Контролируемая страница вне согласованного списка по-прежнему не может встроить документ.
- Необходимые ограничения остальных маршрутов и элементов сохранены.
Не используйте событие load рамки как единственное доказательство успеха: согласно документации iframe, браузер может вызвать его и при неудачной загрузке содержимого. Проверяйте фактическое отображение и подтверждённый результат взаимодействия. Автоматический индикатор, который загорается только по load, способен скрыть проблему.
Если исправление зависит от новой версии заголовков, убедитесь, что проверяете обновлённый ответ. Сохранённый документ или промежуточный кеш могут оставить старую политику. Обновление и очистку кеша проводят по правилам инфраструктуры для затронутых ресурсов, затем повторяют внешний запрос и браузерный сценарий.
Закрепите правила встраивания в сопровождении сайта
Оформите изменение как часть конфигурации интеграции: владелец документа, разрешённые предки, маршруты, поддерживаемые браузеры и способ возврата. Укажите, какая команда отвечает за родительскую страницу, а какая — за виджет. Это позволяет быстро разобрать повторную проблему после смены домена, CDN или официального embed-адреса.
Сохраните образец ожидаемых заголовков и перечень контрольных действий. После значимого обновления повторите проверку вложенности, а не только доступности URL. При удалении партнёрской интеграции пересмотрите разрешения: список origin должен отражать действующие отношения, а не историю всех временных стендов.
Для посетителя предусмотрите понятное поведение при недоступном встроенном сервисе: объяснение ограничения и согласованный альтернативный переход, если он поддерживается. Пустая рамка без сообщения не помогает понять, можно ли завершить действие. Альтернативный путь также проверяют, сохраняя ограничения доступа сервиса.
Доступность документа дополняйте отдельной проверкой его разрешённого встраивания: обычный HTTP-ответ не подтверждает, что виджет открылся в нужной цепочке iframe и выполнил пользовательское действие.
Подключите SiteGuard для внешнего HTTP-контроля доступности важных страниц сайта.