写页面的时候,”点链接别把当前页顶掉”几乎是个固定需求。多数人的第一反应是给 a 标签补一个 target=”_blank”,刷新一看,确实新开了标签页,于是收工。我以前也这么干,直到有一次排查一个诡异的跳转问题,才发现这一行只做了一半的事。
完整的写法长这样
新窗口打开不是加一个属性就完事,标准姿势是三个属性一起上:
<a href="https://example.com"
target="_blank"
rel="noopener noreferrer">示例站点</a>
这三个属性各管一摊:href 决定去哪,target 决定在哪打开,rel 决定新旧两个页面之间还剩多少关系。少写 rel,前面两个照样生效,页面表现上看不出任何区别——问题恰恰就藏在这种”看不出区别”里。

不写 rel,会留下一个后门
浏览器为了兼容老页面,凡是用 target=”_blank” 打开的页面,里面的 window.opener 都会保留一个指向原页面的引用。也就是说,新页面拿着这个句柄,能反过来改你的地址栏、读你的 DOM,甚至用死循环把你的主线程拖死。
这就是常说的反向标签劫持(tabnabbing):用户从你的站跳出去,落到某个看起来很正常的页面,那个页面悄悄把原标签换成仿冒的登录页。用户切回来,账号密码就交出去了。整个过程标签图标和标题都还在,普通用户根本察觉不到。
noopener 和 noreferrer 到底谁在干活
noopener:把新页面的 window.opener 置成 null,句柄直接断掉。解决上面的问题,靠的就是它。
noreferrer:跳转时不发送 Referer 请求头,目标站点看不到用户是从哪个页面来的,属于隐私保护;顺带一提,在一些不支持 noopener 的老浏览器里,它也能起到断开页面关联的作用。
两个一起写,理由很实在:noopener 是主防护,noreferrer 负责补位和隐私,彼此不冲突,代价也就是十几个字符。新版本的 Chrome、Firefox、Safari 虽然已经默认把 target=”_blank” 当成 noopener 处理,但你的代码终究要跑在别人那台未必更新的浏览器上,显式写上永远不亏。
JS 里动态拼链接,最容易把 rel 拼丢
用字符串拼 HTML 是丢属性的重灾区,尤其是图省事写成下面这样:
el.innerHTML += '<a href="https://example.com" target="_blank">站点</a>';
拼的时候少写一项没人提醒你,跑起来也不报错。动态创建链接老老实实用 DOM API,一个属性一次赋值:
const link = document.createElement('a');
link.href = 'https://example.com';
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.textContent = '示例站点';
console.log(link.outerHTML); // 上线前瞄一眼,三项都在
document.body.appendChild(link);
那行 console.log 不是凑数,outerHTML 打出来扫一眼,href、target、rel 齐不齐一目了然,比事后靠经验回忆靠谱得多。
整站外链要统一处理
站点里外链一多,逐个改不现实,批量刷一遍更省事:
document.querySelectorAll('a[href^="http"]:not([href*="yoursite.com"])')
.forEach(a => {
if (a.target === '_blank') {
a.setAttribute('rel', 'noopener noreferrer');
}
});
这里有个小坑:setAttribute 会覆盖掉元素上原有的 rel。如果链接本来就带着 nofollow 之类,记得一并带上,写成 “noopener noreferrer nofollow”,别把 SEO 那边的设置给冲了。
这条捷径别走
在 head 里写一行
几种写法
|
写法 |
实际效果 |
结论 |
|---|---|---|
|
只写 target=”_blank” |
新窗口打开,新页面仍持有 opener |
不合格,必须补 rel |
|
target + rel=”noopener” |
新窗口打开,句柄断开 |
可用 |
|
target + rel=”noopener noreferrer” |
新窗口打开,句柄断开且不传 Referer |
推荐 |
|
|
全站链接新开,rel 无法逐个配置 |
不推荐 |
上线前的自查清单
-
全站搜一遍 target=”_blank”,确认每个旁边都跟着 rel。
-
用 JS 生成的链接,检查是不是还在拼字符串。
-
后台编辑器、富文本里存进去的外链,也要过一遍这道流程。
-
提交前用 outerHTML 或 DevTools 抽查两三个真实链接,别只看源码。
-
顺手确认目标链接是否真的需要新开:同站内跳转留在本页,对用户体验更友好。

