rel="noopener"是防劫持的硬性开关,必须与target="_blank"同时使用才生效;单独存在被忽略,拼写须小写无空格,旧版Safari中noreferrer不能替代它。

rel="noopener" 是防劫持的硬性开关,不是可选项
不加 rel="noopener",target="_blank" 就等于把原页面的 window.opener 对象白送给新页面——跨域也生效。恶意页面能立刻执行 window.opener.location.replace() 把你后台页面无声跳转到钓鱼页,或用 window.opener.document.write("") 清空 DOM。这不是理论漏洞,TikTok 2023 年真实被利用过。
关键点:
-
rel="noopener"必须和target="_blank"(或其他非_self值)同时出现才生效;单独写它,浏览器直接忽略 - 旧版 Safari(≤12.0)和部分 WebView 中,
rel="noreferrer"不保证window.opener === null,不能替代noopener - 拼写必须小写、无连字符:
rel="NOOPENER"、rel="no-opener"、rel="noopener "(末尾空格)全部无效 - Vue/React 中动态绑定要确保最终渲染为字符串,比如
:rel="['noopener', 'noreferrer'].join(' ')",而不是:rel="['noopener']"
rel="noreferrer" 是隐私控制,但附带安全副作用
rel="noreferrer" 的核心作用是阻止 Referer 请求头发送——不是隐藏,是压根不发。它让目标站点无法知道用户从你哪个 URL、哪条路径点进来,避免敏感路径(如带 token 的后台地址)泄露。
但它有个隐式行为:在 Chrome/Firefox/Safari 等现代浏览器中,rel="noreferrer" 会附带启用 noopener 效果。但这不是规范强制行为,不可依赖——尤其旧版 Edge 和某些 WebView 下可能只清 Referer,不切断 opener。
立即学习“前端免费学习笔记(深入)”;
常见误判:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 以为加了
noreferrer就安全了,结果漏掉noopener,在 Safari ≤11.0 下 opener 仍可被利用 - 在需要 Referer 做权限校验的场景(如内部系统跳转),加
noreferrer会导致后端鉴权失败 - 用
referrerpolicy="no-referrer"替代rel="noreferrer"时,它不处理window.opener,也不影响右键“在新标签页打开”行为
为什么必须写成 rel="noopener noreferrer",顺序不能乱
顺序影响解析兼容性。部分 Markdown 渲染器、CMS 或富文本编辑器(如 CKEditor)在解析 rel 属性时,会按空格分割后取第一个合法值。如果写成 rel="noreferrer noopener",可能只识别 noreferrer 而忽略 noopener,导致安全失效。
正确写法只有这一种:rel="noopener noreferrer"(小写、空格分隔、无逗号、无引号内多余空格)。
实际风险高发区不在手写 HTML,而在这些地方:
- 富文本编辑器导出的 HTML 自动加了
target="_blank",但完全不补rel - 评论区、UGC 内容通过正则自动注入
target="_blank",却没做rel合并逻辑 - 服务端模板拼接链接时硬编码
target="_blank",但忘记检查是否已有其他rel值(比如已有rel="nofollow",直接覆盖会丢失语义) - 第三方组件库(如旧版 Ant Design
Link)默认不加rel,升级前需手动 patch
window.open() 场景下 noopener 怎么配才有效
rel 是 HTML 属性,对 JS 主动开窗不起作用。window.open(url, "_blank", "noopener") 是错的——noopener 不是合法的 features 字符串参数。
正确做法分情况:
- 同源且支持现代浏览器:用
window.open(url, "_blank", "noopener=yes")(Chrome/Edge 支持),再配合 HTML 里rel="noopener noreferrer"双重保险 - 跨域或兼容性要求高:改用
location.replace()跳转 + 新窗口监听,或直接放弃 JS 开窗,回归<a>标签 - 绝对不要只写
window.open(url)或window.open(url, "_blank"),默认保留window.opener引用
真正难的不是语法,而是所有动态出口——CMS 输出、Markdown 渲染、用户粘贴、第三方 SDK 注入——都得守住这一关。漏掉任意一个,就等于在防火墙上留了个洞。


















