12k
All articles

Por que rel='noopener' Está Obsoleto para Links

Por que rel=noopener ficou obsoleto em links target=_blank, o que era o reverse tabnabbing e quando noreferrer, opener ou COOP ainda importam.

OpenReplay Team
OpenReplay Team
Por que rel='noopener' Está Obsoleto para Links

Para um link target="_blank" simples, rel="noopener" agora é redundante: todas as versões atuais de Chrome, Edge, Firefox e Safari aplicam o comportamento de noopener automaticamente, então um target="_blank" isolado já define window.opener como null.

Se você ainda digita isso por memória muscular, ou vê o linter sinalizar aquela única âncora que você esqueceu, está remendando um buraco que o navegador fechou anos atrás. Este artigo explica a vulnerabilidade que ele pretendia prevenir, quando os navegadores tornaram a correção padrão, e os casos precisos em que rel ainda faz trabalho real: noreferrer (não automático), rel="opener" (para voltar a habilitar) e o header Cross-Origin-Opener-Policy para controle em todo o site.

Principais Conclusões

  • Em navegadores modernos, um target="_blank" isolado já anula window.opener, então adicionar rel="noopener" manualmente é defesa em profundidade para um buraco que o navegador já fechou.
  • O noopener implícito chegou em etapas (Safari em 2018–19, Firefox 79 em meados de 2020, Chromium 88 no início de 2021) e agora faz parte do padrão HTML da WHATWG.
  • O noopener implícito cobre aproximadamente 95% do uso global de navegadores, segundo o caniuse.com.
  • noreferrer não é implícito: ele ainda remove o header Referer e também implica noopener, então adicione-o apenas quando quiser privacidade de referrer.
  • Use rel="opener" para voltar a habilitar window.opener, e Cross-Origin-Opener-Policy: same-origin para cortar o compartilhamento de opener em todo um documento em um único lugar.

O problema original: reverse tabnabbing

Antes de os navegadores mudarem o padrão, um link target="_blank" entregava à página recém-aberta uma referência ativa de volta para a página que a abriu. Reverse tabnabbing é o ataque que explora isso: a página de destino lê window.opener e redireciona a aba original para um clone de phishing enquanto o usuário está focado na nova aba. A explicação canônica de Mathias Bynens sobre o problema resume tudo de forma direta: onde quer que window.opener exista, a página aberta pode direcionar a página que a abriu para outro lugar, qualquer que seja a origem à qual cada página pertença.

O exploit é uma única linha executada no documento aberto:

if (window.opener) {
  window.opener.location = 'https://you-re-hacked.com';
}

O detalhe crítico é que isso funciona entre origens diferentes. Ler e escrever window.opener.location não é bloqueado quando as duas páginas vêm de hosts diferentes, então nem a same-origin policy nem o CORS impedem o redirecionamento da página que abriu. Isso tornava a situação perigosa em qualquer lugar onde você renderizasse links gerados por usuários ou de terceiros (fóruns, comentários, campos de perfil) em que um atacante controla o href.

O que mudou: target=“_blank” agora implica rel=“noopener”

Os navegadores corrigiram o padrão. Em elementos <a>, <area> e <form>, um target="_blank" agora carrega o mesmo efeito de escrever rel="noopener" você mesmo: o documento aberto recebe null de window.opener, sem necessidade de atributo. Esse comportamento está escrito na especificação HTML da WHATWG, cujas regras para seguir um hyperlink tratam qualquer target _blank como noopener, a menos que o link opte por sair disso com rel="opener". A OWASP agora aponta os leitores para esse mesmo padrão padronizado e trata o ataque como largamente encerrado em navegadores evergreen.

A mudança foi implantada ao longo de cerca de três anos, então “navegadores modernos fazem isso” é uma linha do tempo, não uma data única:

EnginePrimeira versão estável com noopener implícitoData aproximada de lançamento
Safari / WebKitSafari 12.1 (preview no Tech Preview 68)Final de 2018 – 2019
Firefox / GeckoFirefox 79Meados de 2020
Chromium (Chrome, Edge)Chrome/Edge 88Início de 2021

Observe que essas versões descrevem o comportamento implícito, não quando o atributo rel="noopener" em si passou a ser suportado. Isso aconteceu anos antes e é um marco diferente. A tabela de noopener implícito do Caniuse coloca o suporte global em cerca de 95%, com navegadores evergreen cobertos desde por volta de 2018. A fatia restante é pequena, mas real, então verifique suas próprias analytics antes de remover o atributo. O caso remanescente notável é o Edge legado (não-Chromium).

Obsoleto para adicionar manualmente não é o mesmo que inútil

O fato de rel="noopener" ser redundante de digitar não torna todo o atributo rel inútil. A keyword que agora é automática é especificamente noopener. As outras ainda alteram o comportamento:

KeywordO que fazAinda precisa ser digitada em 2026?
noopenerAnula window.opener na página abertaNão, é implícito com target="_blank"
noreferrerRemove o header Referer e implica noopenerApenas quando você quer privacidade de referrer
openerRestaura window.opener (volta a habilitar)Sim, quando você genuinamente precisa da referência

noreferrer não é implícito. Ele ainda suprime o header Referer, então adicione-o apenas quando você realmente quiser ocultar a URL de origem do destino. Ele também traz o benefício de segurança de graça: como noreferrer também anula o opener, adicionar noopener junto não acrescenta nada. Isso torna a combinação comum rel="noopener noreferrer" duplamente redundante em navegadores modernos, já que noreferrer por si só cobre as duas preocupações.

Se você genuinamente precisa que a página aberta mantenha sua referência window.opener (um popup que envia uma mensagem de volta, por exemplo), habilite explicitamente com rel="opener". As release notes do WebKit que introduziram a mudança descrevem isso da mesma forma: o comportamento seguro agora é o padrão, e rel="opener" é como você o reverte deliberadamente.

Uma ressalva honesta sobre suporte legado: adicionar rel="noopener" de qualquer forma é inofensivo. A documentação da auditoria Lighthouse do Chrome observa que escrever o atributo explicitamente ainda oferece alguma cobertura para quem está preso a uma engine mais antiga, como o Edge Legacy. É ruído em navegadores modernos, mas não está errado.

COOP: o controle escalável, em todo o site

Para cortar o compartilhamento de window.opener em um documento inteiro de uma só vez, envie o header de resposta Cross-Origin-Opener-Policy: same-origin em vez de decorar cada link. O header Cross-Origin-Opener-Policy (COOP) decide se um documento de nível superior recém-aberto entra no seu browsing context group ou recebe um próprio. Sob same-origin, documentos cross-origin ficam em um grupo separado e as referências entre eles e a página que os abriu são cortadas, o que fecha o canal do opener uma única vez, de forma centralizada, em vez de âncora por âncora.

# nginx
add_header Cross-Origin-Opener-Policy "same-origin";
// Express
app.use((req, res, next) => {
  res.set('Cross-Origin-Opener-Policy', 'same-origin');
  next();
});

Uma restrição: o COOP é entregue apenas como header de resposta HTTP. Não existe equivalente em <meta http-equiv>, então se você não pode definir headers de resposta na sua infraestrutura, não pode aplicar o COOP. Ele é amplamente suportado nos navegadores atuais e vale a pena ativá-lo como defesa em profundidade. Um session replay de um usuário real clicando em um link externo com target="_blank" é uma forma prática de confirmar que a aba original nunca foi navegada, e de reproduzir qualquer relato de navegação inesperada exatamente no navegador que o usuário estava usando.

O veredito: o que fazer agora

Para código novo voltado a navegadores modernos, não adicione rel="noopener" manualmente; o navegador define isso para você. Você pode relaxar com segurança as regras de lint que o forçam em todo target="_blank", como react/jsx-no-target-blank; mantenha a regra ativada apenas se precisar cobrir o Edge Legacy ou outras engines anteriores a 2021. Adicione rel="noreferrer" quando, e somente quando, quiser suprimir o header Referer. Use rel="opener" no caso raro em que precisa da referência ao opener de volta. Para uma garantia escalável em todo o documento, envie Cross-Origin-Opener-Policy: same-origin.

Vale destacar: orientações anteriores, incluindo o próprio post mais antigo da OpenReplay recomendando rel="noreferrer noopener" em todo link, tratam noopener como algo que você deve sempre digitar e não levam em conta o padrão implícito. Isso reflete um hábito ao qual a indústria se apegou muito depois de os navegadores terem mudado. A posição correta em 2026 é mais restrita: noopener agora é o padrão, então adicioná-lo manualmente é obsoleto, enquanto noreferrer, rel="opener" e o COOP cada um ainda cumpre um papel distinto. Recorra a esses de forma deliberada, e deixe o navegador cuidar do resto.

Perguntas Frequentes

rel='noreferrer' inclui rel='noopener'?

Sim. Definir rel='noreferrer' implica rel='noopener' automaticamente, então ele anula window.opener além de remover o header Referer. Isso significa que a combinação comum rel='noopener noreferrer' é duplamente redundante em navegadores modernos: noreferrer sozinho cobre tanto a anulação do opener quanto a supressão do referrer. Adicione noreferrer apenas quando realmente quiser ocultar a URL de origem da página de destino.

O que acontece se eu omitir completamente rel='noopener' em um link target='_blank' hoje?

Nada perigoso em navegadores modernos. Um target='_blank' isolado já define window.opener como null porque Chrome, Edge, Firefox e Safari aplicam o comportamento de noopener implicitamente, uma regra codificada no padrão HTML da WHATWG. O Caniuse coloca isso em cerca de 95% do uso global de navegadores. O reverse tabnabbing é inerte por padrão; a única lacuna são engines legadas como o Edge Legacy não-Chromium.

Posso definir Cross-Origin-Opener-Policy usando uma meta tag em vez de um header?

Não. O COOP é entregue apenas como header de resposta HTTP, e não existe equivalente em meta http-equiv. Se sua infraestrutura não pode definir headers de resposta, você não pode aplicar o COOP e precisa recorrer aos atributos rel por link ou ao padrão implícito de noopener. Quando você pode definir headers, enviar Cross-Origin-Opener-Policy: same-origin corta o compartilhamento de window.opener em um documento inteiro, em um único lugar central, em vez de decorar cada âncora.

Como volto a habilitar window.opener quando realmente preciso dele?

Use rel='opener' explicitamente no link. Como o comportamento seguro de anular window.opener agora é o padrão do navegador, rel='opener' é a forma de restaurar deliberadamente a referência ao opener, por exemplo quando um popup precisa enviar uma mensagem de volta para a página que o abriu. Isso reverte o comportamento implícito de noopener para aquele link específico, sem afetar os demais.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.