iframe “A conexão foi recusada”: corrigir X-Frame-Options

Ponto-chave

“A conexão foi recusada” dentro de um iframe não é um erro de rede. O site incorporado enviou um header X-Frame-Options ou CSP frame-ancestors dizendo ao seu navegador para não exibi-lo dentro de outra página. A correção fica no servidor do site incorporado, nunca na sua tag iframe: mantenha SAMEORIGIN no site inteiro e abra um único caminho de incorporação com frame-ancestors *.

Verifique agora

X-Frame-Options explicado: um iframe mostrando “A conexão foi recusada” ao lado da correção — SAMEORIGIN no site todo, CSP frame-ancestors * no caminho de incorporação
X-Frame-Options explicado: um iframe mostrando “A conexão foi recusada” ao lado da correção — SAMEORIGIN no site todo, CSP frame-ancestors * no caminho de incorporação
Comece a ler
  1. O que “A conexão foi recusada” dentro de um iframe realmente significa
  2. Como o X-Frame-Options funciona
  3. DENY ou SAMEORIGIN: qual enviar?
  4. ALLOW-FROM morreu — não use
  5. frame-ancestors: a diretiva CSP que o substitui
  6. O que acontece quando os dois headers estão definidos
  7. Passo 1: encontre o header que está bloqueando
  8. Passo 2: decida quem pode incorporar a página
  9. O padrão certo: trave o site, abra um único caminho de incorporação
  10. Nginx (e a armadilha do add_header)
  11. Apache
  12. Next.js e Express
  13. Como incorporar um widget de terceiros com segurança
  14. sandbox: o que permitir em um widget de calculadora
  15. Por que não existe correção do lado de quem incorpora
  16. Verifique os headers a cada mudança
  17. Sintoma → causa → correção
  18. Perguntas frequentes

O que “A conexão foi recusada” dentro de um iframe realmente significa

Você adiciona um <iframe> apontando para outro site, recarrega e, em vez da página, aparece uma caixa cinza com um ícone de rostinho triste; ao passar o mouse, ela diz A conexão com example.com foi recusada. Seu primeiro instinto é suspeitar da rede, porque é exatamente o que o Chrome mostra para um servidor fora do ar. Mas abra o DevTools e o console conta outra história:

text
Refused to display 'https://emicalcs.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.

Nada falhou na rede. O DNS resolveu, a conexão TCP abriu, o TLS terminou e o servidor devolveu um 200 perfeitamente normal. O navegador baixou a página, leu um header de resposta e então se recusou de propósito a exibi-la dentro da sua página. Esse header é o X-Frame-Options, e ele existe para impedir o clickjacking: um ataque em que uma página hostil carrega um formulário de login ou de pagamento dentro de um iframe invisível e engana os visitantes para que cliquem em botões que não enxergam.

Isso importa para o diagnóstico. Uma falha de conexão real — ERR_CONNECTION_REFUSED ao abrir a URL direto numa aba — é problema de servidor ou de firewall, tratado em ERR_CONNECTION_REFUSED. A versão com iframe só aparece quando a página está dentro de um frame, e a mesma URL funciona bem na própria aba. Se o site carrega direto mas não no seu frame, você está diante de uma política de incorporação, não de uma queda.

Veja os headers X-Frame-Options e CSP de uma página

Como o X-Frame-Options funciona

X-Frame-Options é um header de resposta HTTP, descrito pela primeira vez na RFC 7034, uma RFC informativa que o padrão HTML agora substitui, e é suportado por todos os navegadores desde o Internet Explorer 8. O servidor o anexa a uma página; o navegador o lê antes de renderizar e decide se a página pode aparecer dentro de um <frame>, <iframe>, <embed> ou <object> de outra página.

Ele aceita dois valores que ainda importam, mais um que não importa mais:

ValorQuem pode incorporar a páginaUso típico
DENYNinguém — nem o próprio sitePáginas de login, painéis administrativos, tudo que tenha sessão
SAMEORIGINSomente páginas da mesma origem exata (esquema + host + porta)Padrão sensato para a maioria dos sites; permite incorporar suas próprias páginas
ALLOW-FROM https://a.comObsoleto — ignorado por todos os navegadores modernosNão use; veja abaixo

O header vale por resposta, não por site. É o detalhe que a maioria dos guias pula e o que torna possível a correção deste artigo: um servidor pode enviar SAMEORIGIN em 99% das páginas e outra política no único caminho feito para ser incorporado.

DENY ou SAMEORIGIN: qual enviar?

Envie SAMEORIGIN, a menos que tenha um motivo para não fazê-lo. Ele bloqueia todo frame de terceiros e deixa seu site livre para incorporar as próprias páginas — widgets de ajuda, pré-visualizações, painéis que carregam outras páginas internas.

Envie DENY em páginas onde incorporar não tem finalidade legítima e o prejuízo seria grave: formulários de login, páginas de pagamento, ações de um clique como “excluir conta”. O DENY também cobre um caso sutil: um atacante conseguir injetar um frame em uma das suas próprias páginas — com SAMEORIGIN, esse frame injetado ainda seria permitido.

Qualquer que seja a escolha, defina uma vez no servidor web ou no CDN para cobrir todas as respostas, incluindo páginas de erro e arquivos estáticos. Headers definidos no código da aplicação costumam esquecer a página 404 e a de manutenção: defina-os onde todas as respostas passam.

ALLOW-FROM morreu — não use

Artigos antigos ainda recomendam X-Frame-Options: ALLOW-FROM https://partner.com para permitir um único site incorporador. Nunca funcionou no Chrome nem no Safari, e o Firefox o removeu na versão 70 (outubro de 2019). Pior: um navegador moderno que vê ALLOW-FROM trata o header inteiro como inválido e o ignora por completo, deixando a página incorporável por qualquer um — o oposto do que você queria.

Se precisa permitir sites específicos, a resposta é o CSP frame-ancestors, explicado a seguir. O X-Frame-Options só sabe dizer “ninguém” ou “só eu”.

frame-ancestors: a diretiva CSP que o substitui

O Content Security Policy nível 2 introduziu o frame-ancestors, uma diretiva que faz tudo o que o X-Frame-Options faz e acrescenta o que ele não consegue: uma lista de permissão de verdade. Ela vai dentro do header padrão Content-Security-Policy e aceita uma ou mais fontes:

http
# Nobody may frame this (same as X-Frame-Options: DENY)
Content-Security-Policy: frame-ancestors 'none'

# Only my own origin (same as SAMEORIGIN)
Content-Security-Policy: frame-ancestors 'self'

# My origin plus two named partners, one with a subdomain wildcard
Content-Security-Policy: frame-ancestors 'self' https://partner.example https://*.trusted.example

# Anyone may frame this — for a public embed endpoint only
Content-Security-Policy: frame-ancestors *

Três detalhes valem a pena antes de migrar:

  • Ela verifica todos os ancestrais, não só o pai. Se a página A incorpora a página B e B incorpora a sua, tanto A quanto B precisam estar permitidas (CSP). O padrão HTML agora aplica o X-Frame-Options a todos os ancestrais também (WHATWG), mas navegadores antigos verificavam só o pai ou a página de topo.
  • Só funciona como header HTTP. O frame-ancestors é explicitamente ignorado quando a CSP chega por uma tag <meta http-equiv> — o navegador precisa conhecer a política antes de decidir se renderiza o documento. O mesmo vale para o X-Frame-Options. Nenhum dos dois pode ser definido pelo HTML.
  • Curingas valem por host, não globalmente. https://*.example.com casa com qualquer subdomínio de um site; um * sozinho casa com todas as origens da internet. Ótimo para um widget de calculadora, errado para qualquer coisa com sessão.

O que acontece quando os dois headers estão definidos

A maioria dos sites acaba enviando os dois durante uma migração, e a precedência é definida: se frame-ancestors estiver presente, o navegador o aplica e ignora o X-Frame-Options. A especificação CSP nível 2 diz isso sem rodeios, e Chrome, Firefox, Safari e Edge a seguem.

Isso deixa a migração segura simples. Mantenha X-Frame-Options: SAMEORIGIN para os poucos clientes antigos anteriores ao CSP 2, adicione Content-Security-Policy: frame-ancestors 'self' ao lado, e todo navegador moderno usará a regra CSP. Os dois só dão problema se você os fizer dizer coisas diferentes — X-Frame-Options: DENY ao lado de frame-ancestors *, por exemplo — caso em que navegadores modernos permitem a incorporação e só os muito antigos bloqueiam. Mantenha-os coerentes e não há nada a depurar.

Passo 1: encontre o header que está bloqueando

Antes de mudar qualquer coisa, confirme qual política está em jogo. O console já informou o nome do header; o curl mostra o valor exato que o servidor envia. Este exemplo usa o site do caso prático mais adiante — suas páginas principais estão bloqueadas de propósito:

bash
curl -sI https://emicalcs.com/ | grep -i -E 'x-frame-options|content-security-policy'

# Output:
# x-frame-options: SAMEORIGIN

Uma de três coisas volta:

  • x-frame-options: SAMEORIGIN ou DENY — a política clássica de incorporação. Só uma mudança nesse servidor pode removê-la.
  • content-security-policy: … frame-ancestors … — uma política CSP. Leia a lista de fontes; se a sua origem não estiver nela, você está bloqueado. A diretiva costuma ficar enterrada num header CSP longo, junto de script-src e companhia, então leia o valor inteiro.
  • Nenhum dos dois headers: o site incorporado permite o frame e “A conexão foi recusada” tem outra causa: um redirecionamento para uma página que está bloqueada (muitas vezes um login; siga com curl -sIL), ou um header Cross-Origin-Embedder-Policy: require-corp na sua própria página, que bloqueia qualquer frame que não concorde e mostra a mesma caixa cinza.

Passo 2: decida quem pode incorporar a página

Tudo daqui para a frente depende de uma pergunta: você controla o site que está sendo incorporado?

Se não, pare por aqui. Não existe solução do lado do cliente — nem o atributo sandbox, nem outro src, nem uma opção de navegador que seus visitantes vão ter. O dono do site decidiu que a página não deve ser incorporada, e o navegador faz cumprir essa decisão por ele. Suas opções são linkar para a página, perguntar ao dono se ele oferece um endpoint de incorporação ou usar um widget oficial, se existir. Muitos serviços que bloqueiam o site principal oferecem um; o exemplo da próxima seção mostra como é.

Se você controla o site, resista à tentação de remover o header do site inteiro. Isso reabre o clickjacking em todas as páginas — inclusive nas que têm sessões e formulários — para consertar uma incorporação em uma só. Em vez disso, decida quais caminhos específicos devem poder ser incorporados e por quem:

SituaçãoHeader a enviar nesse caminho
Ferramenta interna incorporada pelo seu próprio painelframe-ancestors 'self' (mais X-Frame-Options: SAMEORIGIN)
Widget incorporado por alguns parceiros conhecidosframe-ancestors 'self' https://partner-a.com https://partner-b.com
Widget público incorporável (calculadora, mapa, player)frame-ancestors * somente no caminho de incorporação
Todo o restoframe-ancestors 'self' mais X-Frame-Options: SAMEORIGIN

O padrão certo: trave o site, abra um único caminho de incorporação

O exemplo real mais limpo desse padrão que encontrei é o EMICalcs, uma coleção gratuita de 45 calculadoras financeiras indianas — parcela de empréstimo, SIP, imposto de renda, GST e outras — que outros sites são convidados a incorporar. O site principal é travado; um caminho dedicado /embed/ é aberto. Compare os headers dos dois:

bash
# The calculator page a visitor normally uses — locked
curl -sI https://emicalcs.com/income-tax-calculator/ | grep -i -E 'x-frame|frame-ancestors'
# x-frame-options: SAMEORIGIN

# The same calculator on its embed path — open to any site
curl -sI https://emicalcs.com/embed/income-tax-calculator/ | grep -i -E 'x-frame|frame-ancestors'
# content-security-policy: frame-ancestors *

Repare no que a resposta de incorporação não contém: não há header X-Frame-Options em /embed/. É de propósito. Como a diretiva CSP tem precedência, deixar o SAMEORIGIN seria inofensivo nos navegadores modernos — mas confundiria o próximo engenheiro que ler os headers e ainda bloquearia os poucos clientes antigos que só entendem X-Frame-Options. O caminho de incorporação envia uma política, e ela diz uma coisa só.

O resto do site mantém o SAMEORIGIN, então a página inicial, os artigos e as páginas completas das calculadoras continuam protegidos contra clickjacking. As versões incorporáveis são documentos enxutos, sem navegação e sem nada que valha a pena sequestrar, e por isso abri-las para * é uma troca razoável.

O guia de incorporação deles vale a leitura como modelo se você for publicar um widget próprio: uma URL estável por calculadora, uma altura fixa que serve para a maioria, loading="lazy" no snippet e a promessa de que URLs de incorporação aposentadas vão redirecionar em vez de dar 404 — porque cada site que incorpora você confia que a sua URL continue funcionando.

Nginx (e a armadilha do add_header)

Aqui está o mesmo padrão nos três lugares onde a maioria dos sites define headers. Em cada caso, uma regra global trava tudo e uma regra mais específica a substitui no caminho de incorporação. Primeiro o Nginx, porque ele tem uma armadilha:

nginx
# Site-wide default (server block)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Content-Type-Options "nosniff" always;

location /embed/ {
    # WARNING: any add_header here stops ALL add_header inheritance from the
    # server block — re-declare every header you still want on this path.
    add_header Content-Security-Policy "frame-ancestors *" always;
    add_header X-Content-Type-Options "nosniff" always;
}

O comentário não é opcional. O add_header não é mesclado entre níveis: assim que um bloco location declara qualquer add_header, ele deixa de herdar todos os add_header do bloco server. Equipes perdem HSTS ou nosniff no caminho de incorporação assim e só descobrem meses depois, num scan de segurança. Declare de novo o que precisa e deixe o X-Frame-Options fora do bloco de incorporação para que o caminho envie uma política clara. Desde o nginx 1.29.3, add_header_inherit merge pode religar a herança (nginx), mas não use no bloco de incorporação: ele devolveria a esse caminho o X-Frame-Options e o frame-ancestors 'self' do site todo, e com dois headers CSP o navegador aplica os dois, então o frame continua bloqueado.

Apache

A diretiva Header do Apache mescla entre escopos, então você pode definir o padrão uma vez e mexer só nos dois headers que mudam no caminho de incorporação:

apache
# Site-wide default (needs mod_headers)
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Content-Security-Policy "frame-ancestors 'self'"

<LocationMatch "^/embed/">
    Header always unset X-Frame-Options
    Header always set Content-Security-Policy "frame-ancestors *"
</LocationMatch>

Next.js e Express

No Next.js, os headers são declarados em next.config.js. Quando duas regras casam com o mesmo caminho e definem o mesmo header, a última vence — mas um header definido só pela primeira regra ainda se aplica. Para manter o X-Frame-Options totalmente fora do caminho de incorporação, exclua-o da regra global com um lookahead negativo:

javascript
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        // Every path EXCEPT /embed/... gets the locked defaults
        source: '/((?!embed/).*)',
        headers: [
          { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
          { key: 'Content-Security-Policy', value: "frame-ancestors 'self'" },
        ],
      },
      {
        source: '/embed/:path*',
        headers: [
          { key: 'Content-Security-Policy', value: 'frame-ancestors *' },
        ],
      },
    ]
  },
}

No Express, o popular middleware helmet já envia por padrão X-Frame-Options: SAMEORIGIN e uma CSP que inclui frame-ancestors 'self'. Monte-o globalmente e, no router de incorporação, chame res.removeHeader('X-Frame-Options') e defina Content-Security-Policy como frame-ancestors * antes de renderizar. A ordem importa: sua sobrescrita precisa rodar depois do helmet, ou o helmet a sobrescreve.

Como incorporar um widget de terceiros com segurança

Agora, o outro lado da cerca: você é o site que incorpora, o provedor tem um caminho de incorporação aberto e quer que o frame se comporte bem. Um snippet de widget bem feito é assim; é o que o EMICalcs publica em cada página de calculadora:

html
<iframe src="https://emicalcs.com/embed/income-tax-calculator/"
        title="Income Tax Calculator FY 2026-27 — EMICalcs"
        width="100%" height="760"
        style="max-width:680px;border:0;border-radius:16px;overflow:hidden"
        loading="lazy"></iframe>
<p>Free <a href="https://emicalcs.com/income-tax-calculator/" target="_blank" rel="noopener">Income Tax Calculator FY 2026-27</a> by EMICalcs</p>

Cada atributo tem uma função:

  • title — dá nome ao frame para leitores de tela. Sem ele, a tecnologia assistiva anuncia “frame” e mais nada, e auditorias de acessibilidade reprovam a página.
  • width="100%" com um max-width no style — preenche colunas estreitas no celular sem estourar as largas no desktop.
  • Um height fixo — a página que incorpora não consegue ler a altura do conteúdo de um frame de outra origem, então um valor fixo é a única forma de evitar salto de layout durante o carregamento. Defina uma vez por widget.
  • loading="lazy" — adia a requisição até o frame chegar perto da área visível, então um widget no fim de um artigo longo não custa nada no carregamento inicial.
  • border:0; border-radius; overflow:hidden — remove a borda embutida padrão e recorta os cantos para o widget parecer nativo na sua página.

A linha de crédito fica de propósito fora do iframe. Tudo dentro do frame pertence ao documento incorporado — os links são do provedor, na URL do provedor —, então um link dentro do frame não faz parte da sua página para seus leitores nem para o rastreador de um buscador. Se quiser dar crédito à fonte, ou o provedor pedir, coloque um parágrafo simples ao lado do frame no seu próprio HTML, como acima.

sandbox: o que permitir em um widget de calculadora

O atributo sandbox é a proteção de quem incorpora. Adicione-o sem valor e a página incorporada não recebe nada: nem scripts, nem formulários, nem pop-ups, nem navegação da sua janela, e uma origem opaca que desativa seus próprios cookies e armazenamento. Depois devolva apenas o que o widget precisa:

html
<iframe src="https://emicalcs.com/embed/loan-emi-calculator/"
        sandbox="allow-scripts allow-forms allow-same-origin allow-popups"
        title="Loan EMI Calculator — EMICalcs"
        width="100%" height="760" loading="lazy"></iframe>

allow-scripts e allow-forms são o mínimo para qualquer coisa interativa. allow-same-origin deixa a página incorporada manter a própria origem para que seu armazenamento e seus cookies funcionem — numa incorporação de outra origem isso é seguro, porque “a origem dela” continua sendo a do provedor, não a sua. allow-popups só é necessário se o widget abre links em uma nova aba, como faz o link de crédito dentro da incorporação do EMICalcs.

O que você não deve fazer é combinar allow-scripts com allow-same-origin em um frame que aponta para a sua própria origem. O script incorporado pode então subir e remover o próprio atributo sandbox, e você não terá isolado nada.

Por que não existe correção do lado de quem incorpora

Uma busca por “refused to connect iframe” traz três gambiarras populares. Todas são ruins, e vale saber exatamente por quê.

  • Extensões de navegador que removem o X-Frame-Options — só funcionam no seu próprio navegador. Todo visitante do seu site continua vendo a caixa cinza, e enquanto isso você desligou a proteção contra clickjacking em todos os sites que visita.
  • Passar a página pelo seu próprio servidor — você baixa o HTML de terceiros no servidor e o serve a partir da sua origem, de modo que o header nunca chega ao navegador. Isso quebra links relativos, cookies, logins e tudo que depende de scripts; faz de você responsável pelo conteúdo deles; e normalmente viola os termos do provedor. Sites que percebem bloqueiam o IP do seu servidor.
  • Serviços de captura de tela ou “web para imagem” — você recebe uma imagem estática, não uma página funcionando. Serve como miniatura de pré-visualização, não para uma calculadora.

O header é uma declaração do dono do site, aplicada pelo navegador, sobre o que pode acontecer com a página dele. Os únicos caminhos legítimos são os dois já vistos: o dono abre um caminho de incorporação ou você linka para a página. Provedores que querem ser incorporados facilitam — os bons publicam um snippet, mantêm a URL estável e abrem só o caminho seguro de abrir.

Verifique os headers a cada mudança

Headers de incorporação são definidos em um arquivo de configuração e depois esquecidos, e um CDN, um proxy reverso ou um plugin de segurança bem-intencionado pode adicioná-los, removê-los ou duplicá-los a qualquer momento. Depois de publicar, confira de fora da sua rede:

bash
# The locked pages should show SAMEORIGIN and/or frame-ancestors 'self'
curl -sI https://example.com/ | grep -i -E 'x-frame-options|content-security-policy'

# The embed path should show ONLY frame-ancestors, with the sources you intended
curl -sI https://example.com/embed/widget/ | grep -i -E 'x-frame-options|content-security-policy'

# Error pages should carry the header too
curl -sI https://example.com/this-does-not-exist | grep -i -E 'x-frame-options|content-security-policy'

Depois teste como um navegador faz: coloque a URL de incorporação em um <iframe> numa página servida de uma origem diferente — uma página local file:// não serve, pois os navegadores a tratam de forma especial — e abra o console. Um carregamento limpo significa nenhuma linha Refused to display nem Refused to frame.

Se o site está atrás do Cloudflare ou de outro CDN, uma cópia em cache ou uma regra de borda pode levar headers diferentes dos da sua origem. O Verificador de headers HTTP pede a URL a partir do nosso servidor, fora do seu navegador e da sua rede, e mostra X-Frame-Options e Content-Security-Policy com um veredito de incorporação (SAMEORIGIN ou DENY, via CSP frame-ancestors, ou não enviado): um jeito rápido de pegar uma regra de CDN que recolocou SAMEORIGIN no seu caminho de incorporação sem avisar.

Sintoma → causa → correção

Use isto para ir direto à correção em vez de reler o guia todo:

O que você vêO que significaCorreção
Caixa cinza “A conexão foi recusada”, mas a URL carrega numa abaX-Frame-Options ou frame-ancestors bloqueou o frameSó o dono do site pode mudar — use o caminho de incorporação dele ou linke
Console: Refused to display … 'X-Frame-Options' to 'sameorigin'Header clássico; só frames de mesma origem são permitidosNo site incorporado, abra o caminho de incorporação com frame-ancestors * ou uma lista de parceiros
Console: Refused to frame … violates … frame-ancestorsA lista de permissão da CSP não inclui a sua origemAdicione a sua origem às fontes frame-ancestors do site incorporado
Funcionava ontem, quebrou hoje, sem mudança de códigoUm CDN ou plugin de segurança começou a injetar SAMEORIGINConfira com curl; exclua o caminho de incorporação na regra do CDN ou do plugin
Seu próprio painel não consegue incorporar sua própria páginaSubdomínio ou esquema diferente (www x domínio raiz, http x https)Use frame-ancestors 'self' https://example.com https://*.example.com (o curinga não cobre o domínio raiz)
A incorporação funciona, mas HSTS ou nosniff sumiram nesse caminhoA herança do add_header do Nginx os descartouDeclare de novo todos os headers dentro do bloco location
ERR_CONNECTION_REFUSED ao abrir a URL diretoUma falha de rede real, não uma política de incorporaçãoVeja ERR_CONNECTION_REFUSED e o verificador de portas

Se a URL também falha na própria aba, o frame é pista falsa. Percorra as camadas de rede em ordem — DNS, porta, TLS, headers — com o guia para testar um endpoint de API público antes de mexer em qualquer header de incorporação.

Veja todos os headers de segurança que uma URL envia

Confira X-Frame-Options, Content-Security-Policy, HSTS e o resto de qualquer página, buscados dos nossos servidores, para que uma extensão do navegador ou um proxy da sua própria rede não altere o que você vê. Grátis, sem cadastro.

Fontes

  1. RFC 7034: HTTP Header Field X-Frame-Options, IETF
  2. CSP Level 3: frame-ancestors, W3C
  3. HTML Standard: the X-Frame-Options header, WHATWG
  4. ngx_http_headers_module (add_header, add_header_inherit), nginx

Perguntas frequentes

O X-Frame-Options está obsoleto?

Funcionalmente ele foi substituído pela diretiva CSP frame-ancestors, que os navegadores priorizam quando os dois headers estão presentes. Mas não é inútil: todo navegador atual ainda respeita o X-Frame-Options quando frame-ancestors está ausente, e ele protege os poucos clientes antigos anteriores ao CSP nível 2. O conselho prático é enviar os dois e mantê-los coerentes — SAMEORIGIN junto de frame-ancestors 'self'.

O que significa X-Frame-Options: DENY?

DENY diz ao navegador que a página nunca pode ser renderizada dentro de um elemento frame, iframe, embed ou object em nenhuma página — inclusive nas do mesmo site. É a configuração mais rígida e a certa para formulários de login, páginas de pagamento e painéis administrativos, onde não há motivo legítimo para incorporar a página e o clickjacking seria grave.

Qual a diferença entre DENY e SAMEORIGIN?

DENY bloqueia toda incorporação. SAMEORIGIN permite incorporar só por páginas da mesma origem exata — mesmo esquema, host e porta — e bloqueia todas as outras. SAMEORIGIN é o padrão sensato para o site todo porque barra o clickjacking de terceiros e ainda deixa o seu site incorporar as próprias páginas; DENY é para o punhado de páginas que nunca devem ser incorporadas.

Como contornar o X-Frame-Options para incorporar um site?

Do lado de quem incorpora, não dá. O header é definido pelo site que você tenta incorporar e aplicado pelos navegadores dos seus visitantes; nenhum atributo de iframe, flag de sandbox ou truque do lado do cliente muda isso. Extensões que removem o header só afetam o seu navegador, e passar a página pelo seu servidor a quebra e normalmente viola os termos do provedor. Os caminhos legítimos são usar um endpoint de incorporação oficial, se o provedor oferecer, ou linkar para a página.

Como definir o X-Frame-Options como DENY?

Adicione-o como header de resposta HTTP no nível do servidor web para cobrir todas as páginas, inclusive as de erro. No Nginx: add_header X-Frame-Options "DENY" always; no Apache: Header always set X-Frame-Options "DENY". Combine com Content-Security-Policy: frame-ancestors 'none' para os navegadores modernos. Não dá para fazer com meta tag: só um header HTTP funciona.

Por que meu iframe diz “A conexão foi recusada” se o site funciona em uma nova aba?

Porque não há nada errado com a conexão. O site devolveu a página normalmente, mas com um header X-Frame-Options ou frame-ancestors que proíbe renderizá-la dentro de outra página. O Chrome mostra a mesma caixa cinza “A conexão foi recusada” que mostra numa falha de rede real, e por isso confunde; a mensagem do console “Refused to display … in a frame” é a pista definitiva.

Posso definir X-Frame-Options ou frame-ancestors em uma meta tag?

Não. Os dois precisam ser entregues como headers de resposta HTTP. O navegador precisa conhecer a política de incorporação antes de decidir se renderiza o documento, e uma meta tag chega tarde demais. O frame-ancestors é explicitamente ignorado em uma CSP meta http-equiv, e o X-Frame-Options em uma meta tag é ignorado por todos os navegadores.

frame-ancestors * deixa meu site inseguro?

Só nas respostas que o enviam. O header é avaliado por documento, então enviar frame-ancestors * em um caminho /embed/ dedicado deixa o resto do site totalmente protegido por SAMEORIGIN ou frame-ancestors 'self'. O risco se limita ao que um atacante ganharia incorporando a própria página de incorporação — que, para um widget sem estado, sem cookie de sessão e sem ações sensíveis, é nada.

Por que o X-Frame-Options: ALLOW-FROM não funciona?

O ALLOW-FROM só foi implementado pelo Internet Explorer, pelo Edge original (versões 12 a 18) e pelo Firefox, que o removeu na versão 70. Chrome e Safari nunca o suportaram. Navegadores modernos tratam um header com ALLOW-FROM como inválido e o ignoram por completo, deixando a página incorporável por qualquer um. Use a diretiva CSP frame-ancestors, que aceita uma lista real de origens permitidas.

Qual header vence se X-Frame-Options e frame-ancestors estiverem definidos?

frame-ancestors. O padrão HTML manda os navegadores ignorarem o X-Frame-Options sempre que uma CSP aplicada contiver frame-ancestors (o CSP nível 2 já recomendava isso), e todos os navegadores atuais fazem assim; uma política Report-Only não conta. Só clientes antigos demais para entender CSP recorrem ao X-Frame-Options, por isso ainda vale enviá-lo — desde que os dois headers concordem.

Sobre o autor

Shaik Vahid

Fundador da DNS Robot. Vahid passou anos no suporte de hospedagem de sites resolvendo domínios, registros DNS, certificados SSL e entrega de e-mails para clientes. Ele cria as ferramentas da DNS Robot e escreve estes guias a partir dos erros que as pessoas realmente encontram.

Verificador de headers HTTP

Analise cabeçalhos de resposta HTTP e classificação de cabeçalhos de segurança