Por qué rel='noopener' es obsoleto en los enlaces
Por qué rel=noopener quedó obsoleto en enlaces target=_blank, qué era el reverse tabnabbing y cuándo aún importan noreferrer, opener o COOP.
En un enlace target="_blank" sin más, rel="noopener" ya es redundante: todas las versiones actuales de Chrome, Edge, Firefox y Safari aplican el comportamiento noopener de forma automática, por lo que un simple target="_blank" ya establece window.opener en null.
Si aún lo escribes por inercia, o ves cómo el linter señala el único ancla que olvidaste, estás parcheando un agujero que el navegador cerró hace años. Este artículo explica la vulnerabilidad que pretendía evitar, cuándo los navegadores convirtieron la corrección en el comportamiento predeterminado y los casos precisos en los que rel sigue cumpliendo una función real: noreferrer (no es automático), rel="opener" (para volver a habilitarlo) y el encabezado Cross-Origin-Opener-Policy para un control a nivel de sitio.
Puntos clave
- En los navegadores modernos, un simple
target="_blank"ya anulawindow.opener, por lo que añadirrel="noopener"a mano es defensa en profundidad para un agujero que el navegador ya cerró. - El
noopenerimplícito se implementó por etapas (Safari en 2018-19, Firefox 79 a mediados de 2020, Chromium 88 a principios de 2021) y hoy forma parte del estándar HTML de WHATWG. - El
noopenerimplícito cubre aproximadamente el 95 % del uso global de navegadores, según caniuse.com. noreferrerno es implícito: sigue eliminando el encabezadoReferery además implicanoopener, así que añádelo solo cuando quieras privacidad del referente.- Usa
rel="opener"para volver a habilitarwindow.opener, yCross-Origin-Opener-Policy: same-originpara cortar el uso compartido del opener en todo un documento desde un único lugar.
El problema original: reverse tabnabbing
Antes de que los navegadores cambiaran el comportamiento predeterminado, un enlace target="_blank" entregaba a la página recién abierta una referencia activa a la página que la abrió. El reverse tabnabbing es el ataque que explota esto: la página de destino lee window.opener y redirige la pestaña original a un clon de phishing mientras el usuario está concentrado en la pestaña nueva. La explicación canónica del problema de Mathias Bynens lo resume con claridad: donde exista window.opener, la página abierta puede llevar a la que la abrió a cualquier otro sitio, sin importar a qué origen pertenezca cada una.
El exploit es una sola línea ejecutándose en el documento abierto:
if (window.opener) {
window.opener.location = 'https://you-re-hacked.com';
}
El detalle crítico es que esto funciona entre orígenes distintos. Leer y escribir window.opener.location no se bloquea cuando las dos páginas provienen de hosts diferentes, así que ni la política del mismo origen ni CORS impiden redirigir a la página que abrió el enlace. Eso lo volvía peligroso en cualquier lugar donde se renderizaran enlaces generados por usuarios o de terceros (foros, comentarios, campos de perfil) en los que un atacante controla el href.
Qué cambió: target=“_blank” ahora implica rel=“noopener”
Discover how at OpenReplay.com.
Los navegadores corrigieron el comportamiento predeterminado. En los elementos <a>, <area> y <form>, un target="_blank" tiene ahora el mismo efecto que escribir rel="noopener" uno mismo: el documento abierto recibe null al consultar window.opener, sin necesidad de ningún atributo. Ese comportamiento está recogido en la especificación HTML de WHATWG, cuyas reglas para seguir un hipervínculo tratan cualquier destino _blank como noopener, salvo que el enlace lo desactive con rel="opener". OWASP ahora remite a ese mismo comportamiento predeterminado estandarizado y considera el ataque en gran medida cerrado en los navegadores de actualización continua.
El cambio se desplegó a lo largo de unos tres años, así que “los navegadores modernos hacen esto” es una cronología, no una fecha única:
| Motor | Primera versión estable con noopener implícito | Fecha aproximada de lanzamiento |
|---|---|---|
| Safari / WebKit | Safari 12.1 (vista previa en Tech Preview 68) | Finales de 2018 – 2019 |
| Firefox / Gecko | Firefox 79 | Mediados de 2020 |
| Chromium (Chrome, Edge) | Chrome/Edge 88 | Principios de 2021 |
Ten en cuenta que estas versiones describen el comportamiento implícito, no el momento en que el atributo rel="noopener" en sí empezó a ser compatible. Eso ocurrió años antes y es un hito distinto. La tabla de caniuse sobre noopener implícito sitúa la compatibilidad global en torno al 95 %, con los navegadores de actualización continua cubiertos desde alrededor de 2018. La porción restante es pequeña pero real, así que revisa tus propias analíticas antes de eliminar el atributo. El rezagado más notable es el Edge antiguo, previo a Chromium.
Que sea obsoleto escribirlo a mano no significa que sea inútil
Que rel="noopener" sea redundante de escribir no vuelve inútil todo el atributo rel. La palabra clave que ahora es automática es noopener en concreto. Las demás siguen modificando el comportamiento:
| Palabra clave | Qué hace | ¿Sigue siendo necesario escribirla en 2026? |
|---|---|---|
noopener | Anula window.opener en la página abierta | No, es implícita en target="_blank" |
noreferrer | Elimina el encabezado Referer e implica noopener | Solo cuando quieras privacidad del referente |
opener | Restaura window.opener (vuelve a habilitarlo) | Sí, cuando realmente necesites la referencia |
noreferrer no es implícita. Sigue suprimiendo el encabezado Referer, así que añádela solo cuando de verdad quieras ocultar la URL de origen al destino. También aporta el beneficio de seguridad sin coste adicional: como noreferrer también anula el opener, añadir noopener junto a ella no aporta nada. Eso hace que la habitual combinación rel="noopener noreferrer" sea doblemente redundante en los navegadores modernos, ya que noreferrer por sí sola cubre ambas cuestiones.
Si de verdad necesitas que la página abierta conserve su referencia a window.opener (por ejemplo, un popup que envía un mensaje de vuelta), habilítalo explícitamente con rel="opener". Las notas de lanzamiento de WebKit que introdujeron el cambio lo describen igual: el comportamiento seguro es ahora el predeterminado, y rel="opener" es la forma de revertirlo de manera deliberada.
Una advertencia honesta sobre la compatibilidad con navegadores antiguos: añadir rel="noopener" de todos modos es inofensivo. La documentación de la auditoría Lighthouse de Chrome señala que escribir el atributo explícitamente todavía ofrece cierta cobertura para quien siga atado a un motor antiguo como Edge Legacy. Es ruido en los navegadores modernos, pero no es un error.
COOP: el control escalable a nivel de sitio
Para cortar el uso compartido de window.opener en todo un documento desde un único lugar, envía el encabezado de respuesta Cross-Origin-Opener-Policy: same-origin en lugar de decorar cada enlace. El encabezado Cross-Origin-Opener-Policy (COOP) decide si un documento de nivel superior recién abierto se une a tu grupo de contextos de navegación o recibe uno propio. Con same-origin, los documentos de origen cruzado quedan en un grupo separado y se cortan las referencias entre ellos y la página que los abrió, lo que cierra el canal del opener una sola vez, de forma centralizada, en lugar de ancla por ancla.
# nginx
add_header Cross-Origin-Opener-Policy "same-origin";
// Express
app.use((req, res, next) => {
res.set('Cross-Origin-Opener-Policy', 'same-origin');
next();
});
Una limitación: COOP solo se entrega como encabezado de respuesta HTTP. No existe un equivalente con <meta http-equiv>, así que si no puedes establecer encabezados de respuesta en tu infraestructura, no puedes aplicar COOP. Es ampliamente compatible con los navegadores actuales y vale la pena habilitarlo como defensa en profundidad. Una repetición de sesión de un usuario real haciendo clic en un enlace externo con target="_blank" es una forma práctica de confirmar que la pestaña original nunca se redirigió, y de reproducir cualquier reporte de navegación inesperada en el navegador exacto que usaba el usuario.
El veredicto: qué hacer ahora
Para código nuevo dirigido a navegadores modernos, no añadas rel="noopener" a mano; el navegador lo establece por ti. Puedes relajar sin riesgo las reglas de linting que lo imponen en cada target="_blank", como react/jsx-no-target-blank; mantén la regla activa solo si debes dar cobertura a Edge Legacy u otros motores anteriores a 2021. Añade rel="noreferrer" cuando, y solo cuando, quieras suprimir el encabezado Referer. Usa rel="opener" en el raro caso de que necesites recuperar la referencia al opener. Para una garantía escalable a nivel de documento, envía Cross-Origin-Opener-Policy: same-origin.
Cabe señalar que las recomendaciones anteriores, incluida la propia publicación más antigua de OpenReplay que aconsejaba rel="noreferrer noopener" en cada enlace, tratan noopener como algo que siempre hay que escribir y no tienen en cuenta el comportamiento implícito predeterminado. Eso refleja un hábito que la industria mantuvo mucho después de que los navegadores avanzaran. La posición precisa en 2026 es más estrecha: noopener es ahora el comportamiento predeterminado, por lo que añadirlo a mano es obsoleto, mientras que noreferrer, rel="opener" y COOP siguen cumpliendo, cada uno, una función distinta. Recurre a ellos de forma deliberada y deja que el navegador se encargue del resto.
Preguntas frecuentes
¿rel='noreferrer' incluye rel='noopener'?
Sí. Establecer rel='noreferrer' implica rel='noopener' automáticamente, por lo que anula window.opener además de eliminar el encabezado Referer. Esto significa que la habitual combinación rel='noopener noreferrer' es doblemente redundante en los navegadores modernos: noreferrer por sí sola cubre tanto la anulación del opener como la supresión del referente. Añade noreferrer solo cuando realmente quieras ocultar la URL de origen a la página de destino.
¿Qué ocurre hoy si omito rel='noopener' por completo en un enlace con target='_blank'?
Nada peligroso en los navegadores modernos. Un simple target='_blank' ya establece window.opener en null porque Chrome, Edge, Firefox y Safari aplican el comportamiento noopener de forma implícita, una regla codificada en el estándar HTML de WHATWG. Caniuse sitúa eso en torno al 95 % del uso global de navegadores. El reverse tabnabbing es inofensivo por defecto; la única brecha son los motores antiguos como Edge Legacy, previo a Chromium.
¿Puedo establecer Cross-Origin-Opener-Policy con una etiqueta meta en lugar de un encabezado?
No. COOP solo se entrega como encabezado de respuesta HTTP y no existe un equivalente con meta http-equiv. Si tu infraestructura no puede establecer encabezados de respuesta, no puedes aplicar COOP y debes recurrir a los atributos rel por enlace o al comportamiento noopener implícito predeterminado. Cuando sí puedes establecer encabezados, enviar Cross-Origin-Opener-Policy: same-origin corta el uso compartido de window.opener en todo un documento desde un único lugar central, en vez de decorar cada ancla.
¿Cómo vuelvo a habilitar window.opener cuando realmente lo necesito?
Usa rel='opener' explícitamente en el enlace. Como el comportamiento seguro de anular window.opener es ahora el predeterminado del navegador, rel='opener' es la forma de restaurar deliberadamente la referencia al opener, por ejemplo cuando un popup necesita enviar un mensaje de vuelta a la página que lo abrió. Esto revierte el comportamiento noopener implícito para ese enlace concreto sin afectar a los demás.