Codex 能发现 Chrome,却读不到标签页:一次 HTTP 403 排查记录

本文由AI生成,排障过程使用GPT 6.1Sol high配合本人进行,由AI撰写该博客,希望能帮助一些遇到类似问题的人(因为搜了很久都搜不到类似的情况或者解决方案,我一个朋友新装的codex也遇到这个问题了)

这篇文章主要是给自己留档。一个看起来很小的需求——让 Codex 连接 Chrome,读取当前标签页——最后查到了代理、进程环境变量、MCP 插件配置,以及浏览器工具内部的身份请求。

最让人困惑的是:Chrome 可以正常打开网页,Codex 也能发现 Chrome,但一旦让它读取标签页,就报错。更新系统、开启 TUN、修改路由、切换美国节点,都没解决。

最后真正起作用的改动只有两项:在浏览器插件的环境变量允许列表里,加上 HTTP_PROXY 和 HTTPS_PROXY。

先留下最终答案

这次故障里,Codex 主进程已经拿到了代理配置,但浏览器工具的启动器和 Rust supervisor 没有继承这些变量。负责身份请求的进程,因此没有使用我配置的显式 HTTP 代理。

修复的是当前 unified-computer-use 插件的 .mcp.json:

1
2
3
4
5
6
7
{
"env_vars": [
"CODEX_WINDOWS_REGISTERED_CORE",
"HTTP_PROXY",
"HTTPS_PROXY"
]
}

这里展示的是 mcpServers.cua_repl 下的属性片段,不能拿它覆盖整个配置文件。

修改之后,重置浏览器工具,新启动的进程继承了代理变量,并建立了到本地代理的连接。随后 Codex 成功返回 6 个真实 Chrome 标签页,也能读取已有 YouTube 页面的内容。之后在原来的会话里重新验证,仍然成功。

这说明本地修复有效。不过,服务器最初究竟根据哪条规则返回 403,排查并没有查到。下面记录的是这台机器、这些版本下有证据支持的原因,不是“所有 Codex 403 都这样修”的通用结论。

起点:明明发现了 Chrome,为什么还是不能用?

我的环境是 Windows,代理用 v2rayN。排查结束时,版本如下:

项目 版本或配置
Codex Windows 应用 26.1007.2314.0
浏览器运行插件 unified-computer-use 26.1007.21434
内置 Node.js v24.21.0
代理程序 v2rayN / Xray
本地 HTTP 代理 http://127.0.0.1:10808

起初,扩展能提供部分当前网页信息,浏览器发现接口也能返回 Chrome。但这些都不等于完整的浏览器操作链路已经可用。

读取标签页时,看到的是这样的错误:

1
Unexpected token '<', "<html>…" is not valid JSON

单看这句话,很容易往网页 HTML、扩展通信、JSON 解析兼容性上想。我甚至问过:为什么不能直接使用 HTML?

后来才知道,这里的 HTML 并不是正在浏览的网页,而是一个身份接口返回的错误页面。浏览器工具原本期待 JSON,却收到了 HTML。

第一层:把 JSON 报错还原成 HTTP 403

检查本地安装的 browser-service.mjs 后,找到了身份请求:

1
https://chatgpt.com/backend-api/aura/identity

相关逻辑简化后,大致是这样:

1
2
3
4
5
6
const response = await runtime.fetch(identityUrl, options);
const identity = await response.json();

if (!response.ok || !validUserIdentity(identity)) {
throw new Error("User unavailable");
}

顺序有问题:代码先解析 JSON,再检查 HTTP 状态。如果服务器返回的是 HTML 错误页,就会提前在 response.json() 处失败,真正的状态码被藏在了解析错误后面。

这次先做了一处有备份的本地诊断补丁:检查 HTTP 状态,再尝试解析,并输出经过整理的错误信息。身份校验逻辑仍然保留。

重新测试后,错误终于变得明确:

1
2
3
4
Browser identity request failed: HTTP 403;
content-type=text/html; charset=UTF-8;
cf-mitigated=none;
endpoint=/backend-api/aura/identity

注意,这个补丁只让报错更清楚,当时连接仍然没有恢复。

HTTP 403 也不等于“完全访问不到接口”。请求已经收到了响应,问题是请求被拒绝了,返回内容还不是工具期待的身份 JSON。

上面的 cf-mitigated=none 是诊断代码对“没有这个响应头”的表示,不代表服务器明确保证没有任何过滤或策略判断。

为什么发现 Chrome 可以成功?

浏览器发现和读取标签页走的前置条件并不完全一样。源码里,发现用的 getInfo 路径不需要等待同样的身份初始化,而后续会话请求可能要等待它。

于是就出现了这个状态:

1
2
3
4
发现 Chrome:成功
读取标签页:需要身份初始化
身份接口:403 HTML
后续操作:失败

这也是为什么“列表里出现了 Chrome”不能作为修复成功的标准。

第二层:围着代理试了一圈,还是 403

知道失败接口之后,我首先怀疑网络路径。

当时 v2rayN 已经有 OpenAI 的代理规则,其中包含:

1
2
3
4
5
6
7
8
9
10
{
"outboundTag": "proxy",
"domain": [
"domain:openai.com",
"domain:chatgpt.com",
"domain:oaistatic.com",
"domain:oaiusercontent.com"
],
"enabled": true
}

domain:chatgpt.com 覆盖了失败接口的域名。但规则最后还有一条“其余流量直连”,所以排查时尝试把它临时改为代理,保留私有网络的直连例外。

我做过的主要尝试如下:

尝试 当时的结果
更新系统后重新测试 仍然失败
开启 v2rayN TUN 仍然 403
把最终兜底规则改为 proxy 仍然 403
切换美国节点 仍然 403
通过 Codex 的 .env 设置 HTTP 代理 仍然 403

每次重试都清理了浏览器工具里缓存的失败状态,避免只是在重复读取之前已经拒绝的身份初始化结果。

最让我不解的还是:Chrome 明明使用同一个代理,直接打开这个地址却可以成功,为什么 Codex 不行?

当时只能说,Chrome 导航和 Codex 的身份请求不是完全相同的客户端请求;但到底差在哪里,还没有证据。继续换节点,已经不能回答这个问题了。

单独请求接口,也没能给出最终答案

排查中还做了直接请求和显式代理请求的对照。有未认证请求,也有使用已有访问令牌的诊断请求,结果仍然能遇到 403 HTML,部分响应带有 Cloudflare challenge 标记。

这些测试说明不同路径的响应处理有差异,但不能代替浏览器工具自己的正常认证链路。即使单独请求用了已有令牌,也不能假设它的请求上下文与实际工具完全一样。

因此,没有据此认定“账号失效”或者“代理节点一定被封”。真正需要检查的是:实际发送身份请求的进程,到底有没有使用我配置的代理?

第三层:.env 已经设置了,子进程却没有收到

我在 Codex 的 .env 中配置了:

1
2
HTTP_PROXY=http://127.0.0.1:10808
HTTPS_PROXY=http://127.0.0.1:10808

文件位置是:

1
%USERPROFILE%\.codex\.env

但“文件里写了”与“实际请求进程拿到了”之间,还差一次验证。

后续在新会话里,直接检查了运行中相关进程的环境变量,只查看代理相关项目,没有输出完整环境或认证信息。关键结果是:

进程 修复前 修复后
Codex 主 app-server 两个代理变量都有 保持不变
浏览器 MCP 启动器 两个变量都没有 两个变量都有
浏览器 Rust supervisor 两个变量都没有 两个变量都有

还有一个容易误判的细节:部分沙箱子进程里已经能看到代理变量。如果只检查这些进程,或者只在终端里看环境变量,就可能得出“代理已经传进去了”的错误结论。

需要检查的是实际负责 HTTP 请求的进程。

这个请求并不是普通的 Node fetch

浏览器运行时里调用的是 nodeRepl.fetch。本地代码显示,它会把请求序列化成 authenticated_fetch 消息,交给原生桥接后的 Rust supervisor 处理。

简化后的链路是:

1
2
3
4
5
6
7
读取 Chrome 标签页
→ browser-service.mjs
→ nodeRepl.fetch(身份接口)
→ authenticated_fetch 消息
→ Rust node_repl.exe / HTTP 客户端
→ 身份响应校验
→ 返回标签页和网页内容

这就解释了为什么不能看到内置 Node 支持环境代理,便直接认定设置 NODE_USE_ENV_PROXY 能解决问题。普通 Node 的全局 fetch 和这里的原生认证请求,负责网络访问的实现不同。

缺的是插件的环境变量允许列表

当时生效的插件配置文件位于:

1
%USERPROFILE%\.codex\plugins\cache\openai-bundled\unified-computer-use\26.1007.21434\.mcp.json

其中,mcpServers.cua_repl.env_vars 原来只有:

1
2
3
4
5
{
"env_vars": [
"CODEX_WINDOWS_REGISTERED_CORE"
]
}

主进程有代理变量,但这个允许列表没有把它们转交给浏览器工具。这次排查终于从“可能是代理问题”,推进到了一个具体、能验证的本地缺陷。

修复:传递两个变量,而不是继续重装

先备份当前生效的 .mcp.json,再保留其他字段和已有变量,把列表改为:

1
2
3
4
5
6
7
{
"env_vars": [
"CODEX_WINDOWS_REGISTERED_CORE",
"HTTP_PROXY",
"HTTPS_PROXY"
]
}

这次的改动只是在允许列表里补了两个变量名。代理地址继续来自原来的配置,没有在插件中硬编码新地址,也没有绕过身份检查。

如果需要在相同版本下留一份备份,可以先在 PowerShell 中执行:

1
2
3
4
5
6
7
8
# 这里是本次排查的版本;其他安装需要先确认实际生效的版本。
$taskManifest = Join-Path $env:USERPROFILE '.codex\plugins\cache\openai-bundled\unified-computer-use\26.1007.21434\.mcp.json'
$taskStamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$taskBackup = "$taskManifest.bak-$taskStamp"

Copy-Item -LiteralPath $taskManifest -Destination $taskBackup -ErrorAction Stop
Get-FileHash -LiteralPath $taskManifest -Algorithm SHA256
Get-FileHash -LiteralPath $taskBackup -Algorithm SHA256

修改并保存后,可以检查 JSON 能否解析,以及目标字段是否正确:

1
2
$taskConfig = Get-Content -LiteralPath $taskManifest -Raw | ConvertFrom-Json
$taskConfig.mcpServers.cua_repl.env_vars

接下来让 Codex 重置浏览器工具会话,再重新连接 Chrome。本次重置之后,启动器和 supervisor 被自动替换;新进程继承了两个变量。整个过程没有手动结束共享进程,也没有完整重装应用。

如果重置后没有替换相关进程,再保存正在进行的工作并完整退出、重新打开应用。不要把“杀掉所有 node_repl.exe”当成默认步骤,因为其他活动会话也可能在使用它。

修复前后的连接证据

修复前,supervisor 建立的是指向公网 HTTPS 地址的套接字,没有观察到它与配置的本地 HTTP 代理建立显式连接。

修复后,新 supervisor 出现了到以下地址的已建立连接:

1
127.0.0.1:10808

这个监听端口属于 Xray。也就是说,实际请求进程终于使用了配置的显式代理。

这里仍然要留一个边界:看到公网目的地址,并不能单凭这一点认定 TUN 从未截获之前的连接。它能帮助判断显式 HTTP 代理是否被使用,却不能完整重建 TUN 里的流量路径。

什么才算“修好了”?

这次没有把“修改成功”“JSON 语法正确”或者“浏览器列表里出现 Chrome”当作结束。

最终验证做了三件事:

  1. 从真实 Chrome 扩展连接获取标签页,返回了 6 个标签页。
  2. 读取已有 YouTube 页面的可访问性内容,能看到搜索、首页、Shorts、订阅等控件。
  3. 回到原来的会话,重置工具后再次读取,结果仍然正常。

这些操作通过了之前卡住的身份初始化链路,才是连接恢复的证据。它们验证了标签页枚举和网页读取,并不意味着所有网站、所有浏览器操作都做过测试。

这次排查留下的几个答案

是不是服务器根本访问不到?

不是这种意义上的“访问不到”。403 说明客户端收到了拒绝响应。真正阻塞的是身份校验无法得到预期结果。

是不是 v2rayN 的域名规则写错了?

本次规则包含了目标域名。后来发现的是 Codex 浏览器工具的代理变量没有被转发,单独修改路由没有解决这个缺陷。

为什么 Chrome 可以,Codex 不可以?

“都在同一台电脑上”和“我设置了同一个代理”,并不保证两个客户端的实际请求路径相同。本次就是在实际进程环境里找到了差异;客户端认证上下文也不能直接等同。

为什么换美国节点还是不行?

换节点没有补上遗漏的环境变量。此前各次连接在 TUN 中具体如何处理,没有完整还原,所以也不能反过来说节点对所有响应都毫无影响。

为什么旧版本好像能用?

没有做相同环境下的版本对照实验。仅凭记忆中的“以前正常”,不能确定是哪次更新引入了问题。

需不需要卸载重装?

这次不需要。排查中已经备份过会话历史,也准备过重装方案,但最终恢复连接靠的是插件环境变量转发。

以后更新会不会又出现?

这次改动位于有版本号的插件缓存目录。插件更新可能替换文件。如果再次出现同样症状,应先检查新版本实际生效的配置和进程继承情况,而不是继续修改旧目录。

如果要撤销,可以恢复修改前的备份,再重置浏览器工具。恢复前应确认文件没有其他后续修改,避免把新的变更一起覆盖。

另外,临时改成“其余流量全部代理”的路由,在修复后没有单独做回退测试。不能据此认定这条临时规则永远必要,也不能直接认定现在可以无影响地撤销。

给未来自己的排查顺序

如果又看到“Chrome 能发现,但标签页读不了”,我会先记录实际失败阶段和响应状态,再沿着发送请求的进程往下查:

1
2
3
4
配置文件里有代理
≠ 终端里有代理
≠ 应用主进程有代理
≠ 真正发请求的子进程有代理

这次还有两件值得改进的产品行为:浏览器工具应正确接收应用配置的代理变量;身份请求失败时,应先报告 HTTP 状态,再处理 JSON。前者让连接恢复,后者让问题不再被一个 < 字符掩盖。

至少下次再遇到相同症状,我不用从“是不是要重装 Windows”重新开始了。

参考:OpenAI 浏览器扩展排障文档。该文档提供一般排障入口;本文的具体代理继承问题与本地修复来自实际排查记录,并非官方通用修复方案。


Codex 能发现 Chrome,却读不到标签页:一次 HTTP 403 排查记录
https://nekonya.one/2026/10/11/codex-chrome-proxy-403/
作者
Guawazi233
发布于
2026年10月11日
许可协议