能力中的概念
ERR_LOCAL_NETWORK_PERMISSION_MISSING 排查
发布于
公共页面请求本地或 loopback 目标时,如果 Chrome 无法通过权限和地址空间检查,就会产生 Local Network Access 错误。页面 JavaScript 通常只收到被拒绝的 fetch() Promise,DevTools 才可能显示更具体的 Chromium 网络错误。
Chrome 142 上线 Local Network Access 权限提示。Chrome 公告说明首个版本覆盖 fetch()、子资源加载和子框架导航;规范定义安全上下文、权限、Permissions Policy 和目标地址空间检查。BCD 对 Request.targetAddressSpace 的支持记录包括 Chrome 142 及镜像的 Chromium 派生浏览器;完整支持范围见兼容性数据。BCD 未记录 Firefox 或 Safari 支持。
工作原理
链接到“工作原理”一节在 Chrome 首个里程碑中,从公共地址空间发往 local 或 loopback 目标的请求属于本地网络请求。规范要求客户端处于安全上下文,存在关联文档,Permissions Policy 允许访问,并且页面取得 local-network 或 loopback-network 授权。如果权限状态为 prompt,用户代理会询问用户,只有同意后才继续。
Fetch API 不会把 Chromium 内部网络错误码作为结构化 JavaScript 值暴露出来。因此,fetch() 被拒绝只是诊断起点,不能证明用户拒绝了权限。还要查看 DevTools、请求 URL、页面安全上下文、响应的 CORS 标头,以及目标设备是否响应。
| 信号 | 来源给出的含义 | 下一项检查 |
|---|---|---|
net::ERR_LOCAL_NETWORK_PERMISSION_MISSING |
Chromium 因缺少本地网络权限而阻止请求 | 请用户检查该站点的本地网络权限 |
net::ERR_BLOCKED_BY_LOCAL_NETWORK_ACCESS_CHECKS |
Chromium 在 Local Network Access 检查中阻止连接,并把这个错误码留给 CORS 错误路径 | 比较声明与解析后的地址空间,并检查策略 |
fetch() 在 JavaScript 中抛出 TypeError |
Fetch 以网络错误结束 | 配合 DevTools 和服务端日志,区分权限、CORS、混合内容、DNS、拒绝连接和超时 |
| 没有出现权限提示 | 请求可能在提示前失败,或文档没有提示资格 | 检查安全上下文、关联文档和 Permissions Policy |
targetAddressSpace: "local" 或 "loopback" 用来声明目标。规范允许这项声明绕过对应的混合内容升级步骤,连接后再比较解析地址与声明;两者不匹配时返回网络错误。它既不会授予权限,也不会替设备补上 CORS 响应标头,只是一项声明和约束。
worker 需要额外处理。当前规范在请求没有关联 Document 时返回网络错误,旁注明确指出 Service Worker 会受影响。Chromium issue 404887282 跟踪 Service Worker 的 Local Network Access 行为。把网络任务移入 worker 前,应先从文档流程取得授权,并保留文档侧回退。
| 失败边界 | 应调整的内容 |
|---|---|
| 权限被拒绝或缺失 | 说明连接用途,让用户修改站点权限后重试 |
| Permissions Policy 阻止 iframe | 向目标 frame 委派 local-network、loopback-network,或 Chromium 的合并别名 |
| 地址空间声明与 DNS 结果不符 | 修正 targetAddressSpace 或主机名;不要为了访问公共目标而放宽声明 |
| 设备没有允许 CORS | 配置设备 HTTP 响应,允许发起请求的源 |
| 设备离线或拒绝连接 | 提供设备设置和连接检查 |
浏览器没有 targetAddressSpace |
使用 HTTPS 端点或浏览器支持的设备能力,不要假定 Chrome 路径存在 |
下面两个示例都从安全文档开始。它们返回应用状态,不会声称一个 JavaScript TypeError 就能识别具体网络原因。
声明本地目标并保留拒绝结果
链接到“声明本地目标并保留拒绝结果”一节特性检测让不支持的路径不使用 Chrome 专有请求成员。catch 分支记录错误名称,并告诉界面仍需进一步诊断。
<label> 设备状态 URL <input id="device-url" type="url" value="http://192.168.1.1/status"></label><button id="read-device" type="button">读取状态</button><output id="read-result" aria-live="polite"></output>
<script>async function readDeviceStatus(url = 'http://192.168.1.1/status') { if (!window.isSecureContext) { return { ok: false, reason: 'secure-context-required' }; } if (!('targetAddressSpace' in Request.prototype)) { return { ok: false, reason: 'address-space-option-unsupported' }; } try { const response = await fetch(url, { targetAddressSpace: 'local', }); if (!response.ok) return { ok: false, reason: `http-${response.status}` }; return { ok: true, data: await response.json() }; } catch (error) { return { ok: false, reason: 'network-error', errorName: error.name }; }}
const readButton = document.querySelector('#read-device');const deviceUrl = document.querySelector('#device-url');const readResult = document.querySelector('#read-result');
if (readButton && deviceUrl && readResult) { readButton.addEventListener('click', async () => { readButton.disabled = true; const result = await readDeviceStatus(deviceUrl.value); readResult.textContent = JSON.stringify(result); readButton.disabled = false; });}</script>界面现在可以把配置问题和 HTTP 响应分开。若支持人员需要区分权限与地址空间检查,仍需从 DevTools 或受管遥测中取得 Chromium 网络错误码。
worker 失败后从文档重试
链接到“worker 失败后从文档重试”一节按当前规范旁注,Service Worker 没有可靠的提示界面。下面的消息处理器把控制权交还文档,用户点击后由文档发出请求,Chrome 因而有机会显示权限 UI。
<button id="retry-device" type="button" hidden>重试设备连接</button><output id="device-result" aria-live="polite"></output>
<script>const retryButton = document.querySelector('#retry-device');const deviceResult = document.querySelector('#device-result');
function renderDeviceResult(result) { if (!deviceResult) return; deviceResult.textContent = result.ok ? JSON.stringify(result.data) : `设备请求已停止:${result.reason}`;}
if (retryButton) { retryButton.addEventListener('click', async () => { if (retryButton.disabled) return; retryButton.disabled = true; const result = await readDeviceStatus(); renderDeviceResult(result); retryButton.hidden = result.ok; retryButton.disabled = false; });}
navigator.serviceWorker.addEventListener('message', (event) => { if (event.data?.type !== 'local-network-retry-required') return; if (!retryButton) return; retryButton.hidden = false;});
async function requestFromWorker() { const controller = navigator.serviceWorker.controller; if (!controller) return readDeviceStatus(); controller.postMessage({ type: 'read-local-device' }); return { ok: false, reason: 'delegated-to-worker' };}
requestFromWorker().then(renderDeviceResult);</script>worker 的请求被拒绝后,应发送 local-network-retry-required。文档不能保证重试成功;CORS、地址不匹配或设备不可达仍会产生网络错误。Web Bluetooth 兼容性数据展示了另一条需要权限的设备连接路径,适用于提供蓝牙而非 HTTP 服务的设备。
另请参阅
链接到“另请参阅”一节- Local Network Access(wicg.github.io)
- Local Network Access 新权限提示(developer.chrome.com)
- Local Network Access
- Permissions API
- Web Bluetooth API
规范
| 规范 | 状态 |
|---|---|
| 本地网络访问(Request.targetAddressSpace) | WICG 草案 |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop)# | 支持 | 142 | 高 | 来源 | — |
| Chrome (Android)# | 支持 | 142 | 高 | 来源 | 1 |
| Edge (Desktop)# | 支持 | 142 | 高 | 来源 | 2 |
| Firefox (Desktop)# | 不支持 | — | 高 | 来源 | 3 |
| Firefox (Android)# | 不支持 | — | 高 | 来源 | 45 |
| Safari (macOS)# | 不支持 | — | 高 | 来源 | 6 |
| Safari (iOS)# | 不支持 | — | 高 | 来源 | 78 |
| Samsung Internet# | 支持 | 30.0 | 高 | 来源 | 9 |
| WebView (Android)# | 不支持 | — | 高 | 来源 | 10 |
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- browser-compat-data 未记录 Firefox 的支持。
- browser-compat-data 未记录 Firefox for Android 的支持。
- 由 browser-compat-data 镜像自 Firefox 的数据推导。
- browser-compat-data 未记录 Safari 的支持。
- browser-compat-data 未记录 iOS 版 Safari 的支持。
- 由 browser-compat-data 镜像自 Safari 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
- browser-compat-data 未记录 WebView Android 的支持。