rel="noopener"能防窗口劫持,因为它在浏览器解析阶段使新页面的window.opener为null,彻底阻断跨域篡改原页面的能力;必须与target="_blank"配合使用才生效,且拼写必须准确。

rel="noopener"为什么能防窗口劫持
它不是拦截行为,而是从浏览器解析阶段就让新页面的 window.opener 为 null。只要新页面拿不到这个引用,就完全无法调用 window.opener.location.replace()、window.opener.document.write() 或 window.opener.postMessage()——连读都读不到,更别说篡改原页。
这种劫持叫 Tabnabbing,2023 年 TikTok 就因此被真实利用过:用户点开一个广告链接,后台页面就被无声替换成钓鱼登录页。
关键点在于:window.opener 跨域也生效,同源策略不拦 location 跳转,所以不加 rel="noopener" 就等于把原页面的控制权白送给第三方。
rel="noopener"必须和target="_blank"一起用才生效
单独写 rel="noopener" 在 <a> 标签里但没配 target="_blank",浏览器直接忽略。它只在创建新浏览上下文时触发,包括:
立即学习“前端免费学习笔记(深入)”;
<a target="_blank">-
<form target="_blank">(注意:rel属性不适用于<form>,必须改用window.open()并显式传null) -
window.open(url, "_blank")(此时rel无效,得靠features参数)
常见错误:
- 漏掉
target="_blank",只加rel="noopener" - 写成
rel="noopen"或rel="no-opener"(拼写必须是小写、无连字符) - 在
target="_self"上硬塞rel="noopener"(完全无效)
要不要加rel="noreferrer"
rel="noreferrer" 和 rel="noopener" 解决的是不同问题:
-
rel="noopener":安全底线,切断 JS 引用 -
rel="noreferrer":隐私补充,让 HTTP 请求头不带Referer字段(比如防止泄露/admin/users?id=123这类敏感路径)
但要注意:
- 旧版 Safari(≤11.1)中
rel="noreferrer"不保证window.opener === null,不能替代noopener - 如果后端靠
Referer做简单来源校验(如图片防盗链),加了noreferrer可能导致请求被拒 - 推荐组合写法:
rel="noopener noreferrer",顺序别颠倒——部分 CMS 或 Markdown 渲染器只认第一个合法值
动态生成链接最容易漏掉rel属性
手写 HTML 很容易补上 rel,但真实风险高发区在服务端或富文本输出环节:
- 用户粘贴外链,CMS 自动加
target="_blank"却不补rel - Vue 的
v-html或 React 的dangerouslySetInnerHTML渲染富文本时,原始 HTML 没带rel,前端不会自动注入 - 服务端模板拼接链接,
target="_blank"是硬编码,rel却被遗忘 - 已有
rel="nofollow"的链接,直接覆盖成rel="noopener",丢失原有语义
最稳妥的做法是在链接生成环节就强制注入,而不是依赖运行时补全;正则匹配 target="_blank" 且不含 noopener 的标签,统一补上 rel="noopener noreferrer",同时保留已有合法 rel 值(如 rel="author")。
真正难的不是语法对不对,而是每个动态出口都要守住这一关——CMS 输出、评论区、邮件模板、SSR 渲染,漏一处就等于留一个后门。



















