必须配rel="noopener noreferrer",因现代浏览器已强制降级处理仅含target="_blank"的链接,新页面可通过window.opener劫持原页或阻塞其js执行;noopener切断opener引用防tabnabbing,noreferrer阻止referer泄露。

target="_blank" 必须配 rel="noopener noreferrer"
只写 target="_blank" 已经不够用了。现代浏览器(Chrome 88+、Firefox 79+、Safari 16+、Edge 90+)会主动降级处理:要么拦截新开页,要么强制在当前页跳转。这不是 bug,是安全防护机制。
根本原因是新页面可通过 window.opener 修改原页面 URL(tabnabbing 攻击),或阻塞原页面 JS 执行(性能拖累)。rel="noopener" 切断 opener 引用,rel="noreferrer" 还能阻止 Referer 头泄露来源。
-
rel="noopener"是最低要求;加noreferrer更严格,推荐直接写全 - 顺序无关:
rel="noreferrer noopener"和rel="noopener noreferrer"效果一致 - 别写成
rel="noopenner"或漏掉下划线——大小写和拼写必须完全匹配
动态生成链接时容易漏掉 rel 属性
用 JavaScript 拼接字符串(比如 innerHTML += '@#@#@#@#@#@#@#@#@#@0')极易遗忘 rel。一旦漏掉,上线后某个用户点开就可能被拦截或卡死原页。
更稳妥的做法是走 DOM API:
- 用
document.createElement('a')创建元素 - 依次设置
link.href、link.target = '_blank'、link.rel = 'noopener noreferrer' - 插入前检查
link.outerHTML,确认三项都存在 - 服务端模板(如 Jinja、Twig、PHP)中也要统一补全,不能指望前端“兜底”
外部链接需补充可访问性提示
rel="noopener noreferrer" 解决的是安全问题,不是可访问性问题。屏幕阅读器不会告诉用户“这个链接会在新标签页打开”,用户可能误以为当前页已跳转,导致迷失上下文。
WCAG 要求对改变上下文的行为做明确提示:
- 视觉上:在链接文本后加统一图标(如 ↗),并用
aria-hidden="true"隐藏其语义 - 语义上:用
aria-label补充说明,例如aria-label="访问示例网站(在新标签页中打开)" - 避免仅依赖 title 属性——它不被所有读屏软件可靠支持,且无焦点/键盘交互
- PDF/Word 等非 HTML 资源链接,还必须声明格式与大小,比如“年度报告(PDF,2.4 MB)”
哪些场景 target="_blank 会失效
不是所有环境都按规范执行 target="_blank:
- 微信内置 WebView、旧版安卓浏览器(Android 4.x)、部分企业内嵌 WebView:直接忽略
target,得退回到window.open(url, '_blank', 'noopener,noreferrer') - 富文本编辑器(TinyMCE、CKEditor)或 CMS 后台:常过滤
rel属性,需检查后端白名单是否放行 - CSP 策略禁用 popup:控制台报错
Refused to open a window,此时只能改用原生<a></a>标签 - Vue/React 中的封装按钮组件(如
<router-link></router-link>或自定义<buttonlink></buttonlink>):它们不解析target,必须退回原生<a href="..." target="_blank" rel="noopener noreferrer"></a>
真正难的不是第一次写对,而是确保每个外部链接——无论是手写、CMS 输出、JS 注入,还是 Markdown 渲染结果——都稳定携带 href、target="_blank"、rel="noopener noreferrer" 三项,缺一不可。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











