能力 · 概念
Local Network Access
发布于 更新于
Local Network Access 是浏览器在允许公网页面向用户本地网络上的设备(路由器、打印机、开发服务器)或本机发送请求之前征求的权限。页面不需要调用任何 API 去申请它:第一次指向这类地址的 fetch()、子资源加载或 iframe 导航就会触发提示,提示被拒则请求失败。脚本能看到的唯一接口是 fetch() 与 Request 上的 targetAddressSpace 选项,用来声明请求的去向,让浏览器据此免除混合内容拦截。
Chrome 142 默认显示该提示;Chrome 138 起可通过把 chrome://flags/#local-network-access-check 设为「Enabled (Blocking)」提前启用。Request.targetAddressSpace 自 Chrome 142 存在,Firefox、Safari、Android WebView 的任何版本都没有(BCD api.Request.targetAddressSpace)。这一设计取代了 Private Network Access:后者靠本地设备必须应答的 CORS 预检来把关同类请求,Chrome 已将其搁置。
规范把每个 IP 地址归入三个地址空间之一:loopback(127.0.0.0/8、::1/128、0.0.0.0/32、::/128、198.18.0.0/15)、local(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、运营商级 NAT 的 100.64.0.0/10、链路本地的 169.254.0.0/16、fc00::/7、fe80::/10、fec0::/10、0.0.0.0/8,以及 IPv6 文档前缀 2001:db8::/32 与 3fff::/20),其余全部为 public。IPv4 映射的 IPv6 地址(::ffff:0:0/96)按其内嵌 IPv4 地址归类。本地网络请求指目标比发起文档所在空间更不公开的请求:public 到 local、public 到 loopback、local 到 loopback。
两项受策略控制的特性守住这些请求,默认允许列表都是 'self':local-network 管去往 local 地址的请求,loopback-network 管去往 loopback 地址的请求。旧的单一名称 local-network-access 在 Chromium 中仍作为两者的别名保留,Permissions-Policy 响应头和 iframe 的 allow 属性里都能用。因此需要该能力的跨源 iframe 必须以 allow="local-network"(或 allow="local-network-access")嵌入;未委托时其请求直接失败,不出现提示。只有安全上下文才能持有该权限,http:// 页面会被直接拦下。
由于多数本地设备仍只提供明文 HTTP,规范规定:浏览器若能在 DNS 解析前判断这是本地网络请求,就免除混合内容检查。判断依据有三种:主机名是私有 IP 字面量(http://192.168.0.1/)、主机名以 .local 结尾、调用带有 targetAddressSpace: "local" 或 "loopback"。声明会在建立连接后核验:解析出的地址不在声明的空间内时请求失败,而不是悄悄变成公网请求。豁免只针对连接而不针对响应:一个恰好解析到 192.168.0.1 的公网主机名若未加注解,仍算混合内容。
Chrome 对 fetch()、子资源加载(<img>、<script>、CSS)和 iframe 导航执行检查;Service Worker 与 Shared Worker 只有在同源文档已经获得授权后才能发起这类请求(Worker 无法弹出提示,crbug.com/404887282 跟踪修复)。在 Chrome 138 的可选测试阶段,WebSocket、WebTransport 和 WebRTC 连接尚未纳入权限管控(crbug.com/421156866、crbug.com/421216834、crbug.com/421223919)。Chrome 的第一阶段只管控从公网地址发起的请求;https://router.local 这样的本地源访问另一台本地设备或访问回环地址时不会弹出提示,Chrome 博客把这列为计划中的扩展。
两个示例都在 https:// 页面中运行。提示与请求本身绑定,所以每个示例都把被拒绝的 fetch() 当作预期结果,给用户一条手动路径而不是重试。
从已安装的 PWA 读取路由器状态页
Section titled “从已安装的 PWA 读取路由器状态页”'targetAddressSpace' in Request.prototype 是浏览器 fetch 栈是否理解地址空间的唯一运行时信号;它不存在时(Firefox、Safari、Chrome 142 之前),该请求在 HTTPS 页面里就是普通混合内容,浏览器在任何网络活动之前就会拦下。下面的回退逻辑在新标签页打开设备自己的页面,那里不涉及跨源请求。
const ROUTER = 'http://192.168.1.1/status.json';
function browserGatesLocalNetwork() { return typeof Request !== 'undefined' && 'targetAddressSpace' in Request.prototype;}
async function readRouterStatus() { if (!window.isSecureContext || !browserGatesLocalNetwork()) { return { ok: false, reason: 'unsupported' }; } try { const res = await fetch(ROUTER); // IP 字面量:免除混合内容检查,提示只出现一次 if (!res.ok) return { ok: false, reason: `http-${res.status}` }; return { ok: true, status: await res.json() }; } catch { return { ok: false, reason: 'blocked-or-offline' }; // 提示被拒、CORS 失败或设备离线 }}
document.querySelector('#check-router').addEventListener('click', async () => { const result = await readRouterStatus(); if (!result.ok) { window.open('http://192.168.1.1/', '_blank', 'noopener'); return; } renderStatus(result.status);});被拒绝的 Promise 不会说明原因:提示被拒、设备上的 CORS 失败、路由器断电,都表现为 fetch() 抛出的 TypeError。记录原因没问题,但脚本无法据此分支。
为解析到本地地址的主机名声明地址空间
Section titled “为解析到本地地址的主机名声明地址空间”devbox.example.com 这样解析到 10.0.0.5 的公网主机名本身并不豁免混合内容。给调用加上 targetAddressSpace: "local" 后,浏览器允许建立 http:// 连接,再核验解析出的地址确实在 local 空间。不支持该选项的浏览器会忽略这个未知的 RequestInit 成员,同一调用于是退回到被当作混合内容拦截;因此代码在选项缺失时改走 https:// 备选地址。
async function callDevBox(path) { const supportsOption = 'targetAddressSpace' in Request.prototype; const url = supportsOption ? `http://devbox.example.com${path}` : `https://devbox.example.com${path}`; // 设备上需要有效证书
const init = supportsOption ? { targetAddressSpace: 'local' } : {}; const res = await fetch(url, init); return res.json();}
callDevBox('/api/health').catch((err) => { document.querySelector('#devbox-note').textContent = `无法连接开发机(${err.name})。请确认你在办公网络内。`;});如果 devbox.example.com 日后迁到公网 IP,带注解的请求会以 ERR_BLOCKED_BY_LOCAL_NETWORK_ACCESS_CHECKS 失败,而不是把请求泄露到互联网上,这正是声明的意义。
- WebRTC:浏览器中的点对点音视频与数据,尚未纳入该权限的连接类型之一
- WebTransport:HTTP/3 的流与数据报
- Web Bluetooth API:连接蓝牙低功耗设备,完全不经过 IP 的另一条近场设备路径
- Local Network Access: Permissions(wicg.github.io)
- New permission prompt for Local Network Access(developer.chrome.com)
- Request: targetAddressSpace property(developer.mozilla.org)
- Local Network Access explainer(github.com)
规范
| 规范 | 状态 |
|---|---|
| Local Network Access: permission prompt | WICG 草案 |
| Local Network Access: targetAddressSpace fetch option | WICG 草案 |
| Local Network Access: IP address space | WICG 草案 |