一句话总结

如果你在香港或其他网络环境里使用 Codex 时需要代理,但工作内网、学校资源又经常需要 VPN,最省心的做法不是长期打开系统代理,而是只在启动 Codex App 或 Codex CLI 的那个进程里注入代理环境变量。这样代理默认可以关闭,VPN 也不用和全局代理互相打架。

背景:系统代理和 VPN 很容易互相影响

我平时在香港使用 Codex,连接 GPT 服务通常需要走代理。但公司电脑还有另一个现实问题:工作时经常要拨内网 VPN,有时访问学校资源也要拨 VPN。

如果把系统代理长期打开,VPN、内网地址、学校资源和代理软件就容易互相影响。结果是:

  • 浏览器或公司内网访问可能异常;
  • 学校资源可能因为代理出口不对而打不开;
  • Codex 又可能因为 VPN 改了路由,反而连不上模型服务;
  • 每次都手动开关系统代理,很烦,也容易忘。

这几天我试了一种更简单的方式:系统代理保持关闭,只在启动 Codex App 或 Codex CLI 时,让这个进程和它的子进程带上代理配置。实测 Codex App 这条路径很有效;Codex CLI 这边我还会继续观察,但原理一致,可以先按同样方式配置。

先从 ClashX 复制终端代理命令

我用的是 ClashX。ClashX 菜单里有一个「复制终端代理命令」功能,复制出来大概是这一句:

export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890

这句话的意思是:只对当前终端会话设置代理。你可以先在一个终端窗口里执行它,然后测试命令行网络是否能走代理。

但我不建议把这句直接长期写成全局默认值。因为一旦写进 ~/.zshrc 顶层,每个终端、每个 CLI、甚至一些开发工具都可能默认走代理,问题又回到原点。

更好的做法是:把代理设置包进函数里,只有运行这个函数时才生效。

方案一:只让 Codex App 走代理

把下面这段放到 ~/.zshrc

function Codex_APP() {
    (
        export http_proxy="http://127.0.0.1:7890"
        export https_proxy="http://127.0.0.1:7890"
        export HTTP_PROXY="$http_proxy"
        export HTTPS_PROXY="$https_proxy"
        export no_proxy="localhost,127.0.0.1,::1"
        export NO_PROXY="$no_proxy"

        # App 走 Chromium/Electron 的 --proxy-server 参数即可。
        # 避免把 SOCKS / npm / TLS 相关配置误带给其他子进程。
        unset all_proxy ALL_PROXY
        unset npm_config_proxy npm_config_https_proxy
        unset NODE_TLS_REJECT_UNAUTHORIZED

        mkdir -p "$HOME/Library/Logs/com.openai.codex"
        log="$HOME/Library/Logs/com.openai.codex/manual-launch-$(date +%Y%m%d-%H%M%S).log"

        "/Applications/Codex.app/Contents/MacOS/Codex" \
          --proxy-server="http://127.0.0.1:7890" \
          > "$log" 2>&1 &
    )
}

保存后执行:

source ~/.zshrc
Codex_APP

注意:Codex App 更新很频繁,升级后通常会自带重启。升级重启拉起的新进程不会继承你手工启动时注入的环境变量/启动参数,所以这个函数方式更适合「这次启动就走代理」。如果你希望「升级重启之后也继续走代理」,更推荐用下文的 ~/.codex/.env(方案 2A)让 Codex 自己在启动时加载。

重点有三个:

  1. 外层用了 (...) 子 shell,所以这些代理环境变量只在本次启动 Codex App 时生效,不会污染当前终端。
  2. --proxy-server="http://127.0.0.1:7890" 会让 Codex App 这个 Electron/Chromium 进程走指定代理。
  3. 日志会写到 ~/Library/Logs/com.openai.codex/,如果 App 启动失败,可以先看最新的 manual-launch-*.log

上面的路径默认假设 Codex App 安装在 /Applications/Codex.app。如果你和我一样没有装到 Applications 目录,比如放在 ~/code/Codex.app,只需要改这一行:

"/Applications/Codex.app/Contents/MacOS/Codex" \

改成你的实际路径即可:

"$HOME/code/Codex.app/Contents/MacOS/Codex" \

方案二:只让 Codex CLI 走代理

方案 2A(更省事):用 ~/.codex/.env 让 Codex 默认走代理(含 WebSocket 握手)

如果你希望 Codex CLI 启动时就默认带代理(而不是先失败几次再 fallback),可以用 Codex 的 .env 配置文件,把代理地址固定写进去:

  • macOS / Linux:~/.codex/.env
  • Windows:C:\Users\你的用户名\.codex\.env

Windows 用户注意:别让系统把文件搞成 .env.txt(可能被隐藏后缀名坑到)。

文件内容示例(以 Clash 常见的 7890 为例;这一份偏“全量”,你可以按需删减):

HTTP_PROXY="http://127.0.0.1:7890"
http_proxy="http://127.0.0.1:7890"

HTTPS_PROXY="http://127.0.0.1:7890"
https_proxy="http://127.0.0.1:7890"

all_proxy="socks5://127.0.0.1:7890"
ALL_PROXY="socks5://127.0.0.1:7890"

npm_config_proxy="http://127.0.0.1:7890"
npm_config_https_proxy="http://127.0.0.1:7890"

NO_PROXY="localhost,127.0.0.1,::1"
no_proxy="localhost,127.0.0.1,::1"

这套方式的优点是:不用每次写函数/敲命令,Codex 进程一启动就拿到代理配置,WebSocket 握手也更容易一次成功,连接会更快。

另外,它也更适合「Codex 自动升级后自带重启」的场景:只要你没删这个文件,升级重启后依然会按这个默认配置继续走代理。

来源:https://x.com/Suu766/status/2058765726677385338

方案 2B(可切换):用 ~/.zshrc function 只在这次启动时走代理

如果你想保留「默认直连,只有需要时才走代理」的体验,或者希望一键切换直连/代理,那么用函数把代理包在子 shell 里仍然是最稳的方式。把下面这段放到 ~/.zshrc

function Codex_CLI() {
    (
        export http_proxy="http://127.0.0.1:7890"
        export https_proxy="http://127.0.0.1:7890"
        export HTTP_PROXY="$http_proxy"
        export HTTPS_PROXY="$https_proxy"
        export all_proxy="socks5://127.0.0.1:7890"
        export ALL_PROXY="$all_proxy"
        export npm_config_proxy="$http_proxy"
        export npm_config_https_proxy="$https_proxy"
        export no_proxy="localhost,127.0.0.1,::1"
        export NO_PROXY="$no_proxy"

        codex "$@"
    )
}

之后启动 CLI:

source ~/.zshrc
Codex_CLI

如果你确实清楚自己在做什么,并且需要临时跳过审批/沙箱,可以在调用时显式加参数(不建议写成默认):

Codex_CLI --dangerously-bypass-approvals-and-sandbox

.env vs .zshrc function 怎么选?

维度 ~/.codex/.env(方案 2A) ~/.zshrc function(方案 2B)
默认行为 Codex 默认走代理 默认直连,按需用函数走代理
作用范围 仅 Codex(持久生效) 仅本次启动(子 shell,不污染当前终端)
WebSocket 握手 启动即生效(更少失败/更快) 启动即生效(但需要你用函数启动)
适用对象 主要是 Codex CLI Codex CLI(以及 App 的进程级启动方式)
升级重启 升级后重启仍有效(默认配置会被重新加载) 升级后重启可能失效(新进程不一定继承你注入的 env)
切换成本 需要改/临时改名 .env 换命令调用即可

我个人建议:

  • 长期都需要代理,而且希望 Codex 启动“丝滑”→ 用 .env 作为默认方案。
  • 经常在直连/代理之间切换,或者只在少数场景需要代理 → 用 .zshrc function 更灵活。

不建议默认设置 NODE_TLS_REJECT_UNAUTHORIZED=0

有些代理环境里,为了绕过证书问题,会看到这句:

export NODE_TLS_REJECT_UNAUTHORIZED="0"

这会让 Node.js 跳过 TLS 证书校验。它确实可能临时绕过一些公司代理或中间人证书问题,但安全风险很高,不建议默认写进函数。

如果你的环境确实必须这样才能跑通,建议只在临时排障时加,并且明确知道自己在做什么。日常配置里,能不加就不加。

为什么这比开系统代理更稳

这套配置的核心是「进程级代理」:

  • Codex App 通过环境变量和 --proxy-server 走代理;
  • Codex CLI 通过环境变量走代理;
  • 其他应用、浏览器、内网工具、学校 VPN 不受影响;
  • 代理软件可以继续在本机监听 127.0.0.1:7890,但系统代理不需要长期打开。

这样做以后,日常使用路径会变成:

  1. ClashX 保持运行,本地代理端口可用;
  2. macOS 系统代理默认关闭;
  3. 需要 Codex App 时运行 Codex_APP
  4. 需要 Codex CLI 时运行 Codex_CLI
  5. 工作 VPN、学校 VPN 按原来的方式拨号,不再因为全局代理互相干扰。

快速排查

如果配置后 Codex 仍然不能用,可以按下面顺序检查:

  1. 确认 ClashX 本地端口。 默认常见是 127.0.0.1:7890,但你自己的配置可能不同,以 ClashX 复制出来的终端代理命令为准。
  2. 确认 App 路径。 如果不是 /Applications/Codex.app,一定要改成实际路径。
  3. 看启动日志。 Codex App 函数会把日志写到 ~/Library/Logs/com.openai.codex/
  4. 确认函数是否生效。 修改 ~/.zshrc 后,要执行 source ~/.zshrc,或者新开一个终端窗口。
  5. 不要同时开太多层代理。 如果系统代理、VPN、ClashX TUN、终端代理同时开启,排查会变得很困难。建议先从「系统代理关闭 + 只用函数启动 Codex」开始。

小结

对我来说,这个方案解决的是一个很具体但高频的问题:Codex 需要代理,工作和学校资源又需要 VPN,系统级代理一开就容易互相影响。

把代理限制在 Codex App / CLI 启动进程里之后,默认网络环境保持干净,只有 Codex 自己走代理。需要时一条命令启动,不需要时完全不影响其他程序。

这类小配置不复杂,但能显著减少每天在代理、VPN、CLI 之间来回切换的摩擦。