
现代浏览器(2020年后)默认隔离 target="_blank" 打开的页面,导致子窗口无法通过 window.opener 访问父页面。本文详解通过 rel="opener" 显式声明权限、合理设置表单 target 及 rel 属性位置等兼容方案,确保跨窗口通信安全可用。
现代浏览器(2020年后)默认隔离 `target="_blank"` 打开的页面,导致子窗口无法通过 `window.opener` 访问父页面。本文详解通过 `rel="opener"` 显式声明权限、合理设置表单 `target` 及 `rel` 属性位置等兼容方案,确保跨窗口通信安全可用。
在现代 Web 开发中,使用 target="_blank" 打开新页面(如弹窗、表单提交结果页)时,常需子页面与父页面进行交互(例如关闭自身后刷新父页、回传数据等)。但自 Chrome 88、Firefox 85 等版本起,浏览器出于安全考虑(防范反向钓鱼与 window.opener 滥用),默认将 target="_blank" 的新窗口与父窗口解耦:window.opener 被设为 null,且 opener 对象不可写。
幸运的是,这一行为并非强制禁用,而是可通过显式声明 rel 属性来恢复受控访问。核心原则是:必须主动声明 rel="opener"(而非默认隐含),才能赋予子窗口对 window.opener 的只读访问权(注意:仍不可写入父页面 DOM,这是安全底线)。
✅ 正确做法:按场景精准添加 rel="opener"
1. 普通超链接(<a></a> 标签)
只需在 <a></a> 标签中添加 rel="opener" 即可:
<a href="https://example.com/child.php" target="_blank" rel="opener"> 在新窗口打开并保留 opener 引用 </a>
⚠️ 注意:rel="noopener"(常见于防漏洞)会完全禁用 opener;而 rel="opener" 是其“安全启用”对应项,二者互斥。
2. 动态创建的表单(JavaScript)
当通过 JS 构建表单并提交至新窗口时,rel="opener" 必须设置在 <form></form> 元素上,而非 submit 按钮或 input 上:
const form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/childpage.php");
form.setAttribute("target", "formresult"); // 推荐:命名窗口而非 "_blank"
form.setAttribute("rel", "opener"); // ✅ 关键:设在 form 元素
// 添加隐藏字段等...
const input = document.createElement("input");
input.type = "hidden";
input.name = "data";
input.value = "from-parent";
form.appendChild(input);
document.body.appendChild(form);
form.submit();? 为什么用命名窗口(如
"formresult")?
避免每次打开都新建标签页;若目标窗口已存在,则复用并触发window.opener正常绑定,提升体验与可控性。
3. 原生表单 + input[type="submit"]
rel="opener" 不支持写在 <input> 标签内(即使有 formaction/formtarget),必须置于 <form></form> 根元素:
<!-- ❌ 错误:rel 在 input 上无效 -->
<form method="post">
<input type="submit"
value="提交"
formaction="/child.php"
formtarget="_blank"
rel="opener"> <!-- 被忽略 -->
</form>
<!-- ✅ 正确:rel 在 form 上生效 -->
<form method="post" rel="opener">
<input type="submit"
value="提交"
formaction="/child.php"
formtarget="_blank">
</form>⚠️ 重要注意事项
-
rel="opener"仅恢复只读访问:子页面可读取window.opener.location、调用window.opener.postMessage()(需配合origin校验),但禁止直接操作window.opener.document或执行脚本注入(CSP 与同源策略仍严格生效)。 -
HTTPS 环境下更严格:若父页为 HTTPS,子页也必须为 HTTPS,否则
window.opener将被置为null(混合内容限制)。 -
避免
.htaccess强制修复:如问题所述,无法通过服务端头(如Referrer-Policy)绕过该客户端安全机制;所有修复必须在 HTML/JS 层面显式声明。 -
推荐替代方案:对于复杂通信,优先使用
window.postMessage()+message事件监听,它比依赖window.opener更安全、跨域友好且不受此限制影响。
✅ 验证是否生效
在子页面中加入调试代码:
console.log("window.opener:", window.opener);
// ✅ 正常应输出 Window 对象(非 null)
if (window.opener && !window.opener.closed) {
window.opener.postMessage({ type: "childReady" }, "*");
}只要正确配置 rel="opener" 并满足同协议/同源前提,即可稳定恢复跨窗口通信能力——无需降级浏览器或妥协安全性。



















