这是一个关于低压电器知识百科的网站!


这是本文档旧的修订版!


Codex 桌面版单应用代理配置(只让 Codex 走代理)

国内网络下,Codex / ChatGPT 桌面版登录经常会报这一句:

Token exchange failed: token endpoint returned status 403 Forbidden: Country, region, or territory not supported

奇怪的是,浏览器能正常打开 chatgpt.com,代理客户端也开着,偏偏 Codex 登录就 403。

原因是:Codex 的核心 app-server 是 Rust 写的,它只认环境变量 HTTP_PROXY / HTTPS_PROXY / ALL_PROXY,不读 Windows 系统代理。用开始菜单磁贴(AUMID 激活)启动时,它继承的是用户级环境变量,而我们大多数人从没设过 → 登录请求直连出去,被地区拦。

本文的做法:不碰系统代理、不写用户环境变量、不开 TUN,起一个独立的 mihomo 实例,再用一个启动器把代理只注入 Codex 这一个进程树。影响面只有一个程序,删掉快捷方式就回滚。

(以下配置均为脱敏示例,节点栏位请替换成自己的。)

一、先确认是不是这个坑

现象 说明
——
浏览器能开 chatgpt.com 代理本身没问题,问题在“程序没走代理”
Codex 登录报 403,且带 request id 请求直连出去了,被地区拦
报错含 Country, region, or territory not supported 基本可以确诊
换了节点、重装 Codex 都没用 因为流量压根没进代理

判读口诀:用 curl 打一下 token 端点看状态码。

  • 400 / 401:链路是通的,只是参数或凭据不对,属于正常
  • 403:直连出去了,地区被拦
  • 000:根本没连上代理(节点挂了或端口不对)

二、四种方案对比

方案 影响范围 缺点
———
setx HTTPS_PROXY 用户环境变量 所有程序 污染 curl / git / npm / 各种 SDK,且要重新登录
Windows 系统代理 全局 全局生效,且 Codex 的 app-server 根本不读它
TUN 模式 + 进程名规则 按进程 需要管理员,规则常被机场订阅覆盖,排错难
独立实例 + 启动器(本文) 一个进程树 需要自己维护一个 config.yaml

三、原理:为什么"直接运行 exe"就行

实测结论(Windows 11 + MSIX 安装的 Codex):

  • 用 shell:AppsFolder\OpenAI.Codex_xxx!App 这种 AUMID 方式激活:不继承调用者的环境变量,读的是用户环境,所以注入无效
  • 直接运行包内的 ChatGPT.exe:继承调用者的环境变量,codex.exe 作为子进程能读到

所以启动器必须做三件事:

  1. 起自己的 mihomo 实例(独占一个端口)
  2. 给当前 PowerShell 进程设 HTTP_PROXY / HTTPS_PROXY / ALL_PROXY
  3. 直接 Start-Process 那个 ChatGPT.exe,并补一个 --proxy-server=(Chromium 那半边认这个参数)

四、端口规划

同一台机器可能同时跑好几个 mihomo,端口和 DNS 端口都不能撞。

实例 mixed-port DNS 控制口
————
其他全局客户端 7890 — 9090
Codex(本文) 7898 1055 59098
Claude 桌面版 7899 1054 59099

探测端口是否被占(Get-NetTCPConnection 在部分机器上要十几秒,别用它):

foreach ($p in 7890,7898,7899,1054,1055) {
    $c = New-Object System.Net.Sockets.TcpClient
    $iar = $c.BeginConnect('127.0.0.1', $p, $null, $null)
    $ok  = $iar.AsyncWaitHandle.WaitOne(300) -and $c.Connected
    $c.Close()
    "{0,-6} {1}" -f $p, $(if ($ok) { '占用' } else { '空闲' })
}

五、配置文件

目录结构(放在用户目录下):

~/.codex/codex-proxy/
├─ mihomo-codex.exe     # 内核,故意改名,见下方说明
├─ config.yaml          # 端口 / 节点 / 规则
├─ launch-codex.ps1     # 启动器
├─ launch-codex.vbs     # 静默包装,快捷方式指向它
└─ launcher.log         # 启动日志

内核必须改名:很多客户端(含 Claude 桌面版的守护脚本)会用 Get-Process mihomo 按进程名清理,改叫 mihomo-codex.exe 就不会被误杀。

5.1 最小可用配置
mixed-port: 7898
allow-lan: false
mode: rule
log-level: warning
external-controller: 127.0.0.1:59098
tcp-concurrent: true
find-process-mode: always

profile:
  store-selected: true
  store-fake-ip: false

dns:
  enable: true
  listen: 0.0.0.0:1055
  enhanced-mode: fake-ip
  fake-ip-range: 198.20.0.1/16
  default-nameserver: [223.5.5.5, 119.29.29.29]
  nameserver: [223.5.5.5, 119.29.29.29]
  proxy-server-nameserver: [223.5.5.5, 119.29.29.29]

proxies:
  - name: node-vless
    type: vless
    server: 127.0.0.1                 # 换成自己的服务器
    port: 443
    uuid: 00000000-0000-0000-0000-000000000000
    tls: true
    servername: example.com           # 伪装域名(SNI)
    client-fingerprint: chrome
    flow: xtls-rprx-vision
    reality-opts:
      public-key: KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio
      short-id: 01234567
    skip-cert-verify: true
    udp: true

proxy-groups:
  - name: APP
    type: url-test
    proxies: [node-vless]
    url: http://cp.cloudflare.com/generate_204
    interval: 180
    tolerance: 50

rules:
  - MATCH,APP

要点:

  • rules 只写 MATCH,APP:这个实例只服务一个程序,不需要分流;加载 GEOSITE 规则集会在首次启动时下载几十 MB,离线还可能启动失败
  • mixed-port 必须和启动器里的 $ProxyPort 一致,改端口要两边一起改
  • find-process-mode 保持 always,否则控制口看不到进程信息
5.2 三种配节点方式

方式一:手填单个节点

直接改 proxies: 段。注意字段名不能混:VLESS 用 servername,Trojan / AnyTLS 用 sni。写错不报错,但握手失败,表现为端口通了却 curl 000。

方式二:订阅链接(长期用这个)

proxy-providers:
  sub:
    type: http
    url: https://你的订阅地址
    interval: 3600
    path: ./providers/sub.yaml
    health-check:
      enable: true
      url: http://cp.cloudflare.com/generate_204
      interval: 300

proxy-groups:
  - name: APP
    type: url-test
    use: [sub]
    url: http://cp.cloudflare.com/generate_204
    interval: 180

手动触发更新(不用等 interval):

Invoke-RestMethod -Method Put -Uri 'http://127.0.0.1:59098/providers/proxies/sub'

注意:订阅必须是 Clash YAML 格式。base64 或 vmess:// 那种原始订阅,mihomo 吃不下,要先经过订阅转换。

方式三:从现成客户端搬

已在用 Clash / mihomo 系客户端(Clash Verge、FlClash 等)的,可以直接拷它的 proxies: 与 proxy-groups: 段,只改 mixed-port / DNS / 控制口三项。不要整份复制,它的 tun: 和规则会和本实例打架。

5.3 一个容易踩的格式坑

REALITY 公钥要求是 32 字节的 base64url,43 个字符,不带等号。带 = 的普通 base64 会被直接判 invalid REALITY public key,-t 校验就过不去。short-id 是 16 位以内的十六进制。

六、启动器

$ErrorActionPreference = 'SilentlyContinue'
 
$ProxyDir  = Join-Path $env:USERPROFILE '.codex\codex-proxy'
$CoreExe   = Join-Path $ProxyDir 'mihomo-codex.exe'
$ProxyPort = 7898
$PkgName   = 'OpenAI.Codex'
$AppExeRel = 'app\ChatGPT.exe'
$Aumid     = 'OpenAI.Codex_2p2nqsd0c76g0!App'
 
$Proxy = "http://127.0.0.1:$ProxyPort"
 
function Test-Port($p) {
    try {
        $c = New-Object System.Net.Sockets.TcpClient
        $iar = $c.BeginConnect('127.0.0.1', $p, $null, $null)
        $ok = $iar.AsyncWaitHandle.WaitOne(400) -and $c.Connected
        $c.Close(); return $ok
    } catch { return $false }
}
 
# 1) 自己的内核没起就先起
if (-not (Test-Port $ProxyPort)) {
    Start-Process -FilePath $CoreExe -ArgumentList '-d', $ProxyDir -WindowStyle Hidden
    for ($i = 0; $i -lt 40; $i++) {
        Start-Sleep -Milliseconds 500
        if (Test-Port $ProxyPort) { break }
    }
}
 
# 2) 定位 exe(MSIX 包目录每次更新都会变,必须动态取)
$exe = $null
$pkg = Get-AppxPackage -Name $PkgName | Select-Object -First 1
if ($pkg) {
    $c = Join-Path $pkg.InstallLocation $AppExeRel
    if (Test-Path -LiteralPath $c) { $exe = $c }
}
 
# 3) 关掉旧实例,新进程树才会继承新变量
Get-Process ChatGPT -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2
 
# 4) 只给本进程树注入代理,然后直接运行 exe
if ($exe) {
    $env:HTTP_PROXY  = $Proxy
    $env:HTTPS_PROXY = $Proxy
    $env:ALL_PROXY   = $Proxy
    $env:NO_PROXY    = 'localhost,127.0.0.1,::1'
    # --lang=zh-CN 固定界面/菜单语言,见第九节
    Start-Process -FilePath $exe -ArgumentList @(('--proxy-server=' + $Proxy), '--lang=zh-CN')
} else {
    # 兜底:exe 找不到就用 AUMID 激活(没有代理,但至少能开)
    Start-Process ('shell:AppsFolder\' + $Aumid)
}

两个坑:

  • 脚本注释和字符串只能用 ASCII。PowerShell 5.1 会把无 BOM 的 UTF-8 当 ANSI 读,中文注释会导致解析报错(unexpected token)
  • 必须 Start-Process 那个 exe,不能用 AUMID 激活,否则第 4 步注入的变量全部失效

静默包装 launch-codex.vbs(快捷方式指向它,不弹黑窗):

Set sh = CreateObject("WScript.Shell")
ps1 = sh.ExpandEnvironmentStrings("%USERPROFILE%\.codex\codex-proxy\launch-codex.ps1")
sh.Run "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File """ & ps1 & """", 0, False

建快捷方式(桌面 + 开始菜单):

$dir = Join-Path $env:USERPROFILE '.codex\codex-proxy'
$pkg = Get-AppxPackage -Name 'OpenAI.Codex' | Select-Object -First 1
$ws  = New-Object -ComObject WScript.Shell
foreach ($t in @("$env:USERPROFILE\Desktop\Codex (代理).lnk",
                 "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Codex (代理).lnk")) {
    $l = $ws.CreateShortcut($t)
    $l.TargetPath       = 'C:\Windows\System32\wscript.exe'
    $l.Arguments        = '"' + (Join-Path $dir 'launch-codex.vbs') + '"'
    $l.WorkingDirectory = $dir
    $l.IconLocation     = (Join-Path $pkg.InstallLocation 'app\ChatGPT.exe') + ',0'
    $l.Save()
}

以后只用这个快捷方式启动。原来的磁贴启动不带代理,症状就是 403 或一直转圈。

七、验证四步

$d = Join-Path $env:USERPROFILE '.codex\codex-proxy'
 
# 1) 配置语法与节点自检(只校验,不监听)
& "$d\mihomo-codex.exe" -t -d $d
 
# 2) 走它测出口地区(最直接的一步)
curl.exe -s -x http://127.0.0.1:7898 https://www.cloudflare.com/cdn-cgi/trace
 
# 3) 用快捷方式起一次 Codex,然后确认子进程拿到了变量
Get-Process codex | Select-Object Id, StartTime
 
# 4) 看它到底连去哪(有没有绕过代理)
Get-NetTCPConnection -OwningProcess (Get-Process codex).Id |
    Select-Object RemoteAddress, RemotePort, State

正常结果:第 2 步输出里有 loc=US(或你节点所在地),第 4 步的远端地址全部指向代理端口,没有直连的公网 IP。

想省事可以把上面这些做成一个自检脚本,跑完打印 8 项 PASS/FAIL:端口是否监听、进程环境变量、连接数、泄漏连接数、出口地区、token 端点状态码、用户环境变量是否干净、登录凭据文件是否存在。

八、常见问题

症状 原因 处理
———
内核起来几秒就退出 端口或 DNS 端口被占 先跑 -t;换端口,DNS 也要换
curl 走代理返回 000 节点参数错,或节点被墙 换节点;检查 servername 与 sni 是否写混
curl 通,Codex 登录还是 403 用了磁贴启动,走了直连 只用代理快捷方式启动
curl 通,Codex 一直转圈 app-server 没拿到环境变量 查 codex 进程的 HTTPS_PROXY
有连接绕过代理 少了 --proxy-server 参数 启动器加上该参数后重启程序
改了端口启动器没反应 只改了 config.yaml config.yaml 与启动器两处同步改
订阅不更新 订阅链接失效或需 UA 手动 Put 触发一次看报错
菜单栏一会儿中文一会儿英文 只配了 localeOverride,没给 Electron 传 --lang 启动器加 --lang=zh-CN,见第九节

九、固定中文菜单与界面语言

现象:同一个代理快捷方式启动,界面有时中文、有时英文。

原因:Codex 桌面版的语言由两套互相独立的机制决定,只改一处就会飘。

层 决定什么 从哪里读
———
Electron 的 app.getLocale() 顶部菜单栏(文件/编辑/视图/帮助) 启动参数 --lang,不传就按系统/默认推断
config.toml 的 [desktop] localeOverride 应用内部文案(新聊天、项目等) 用户配置文件,空值表示自动探测

做法一:启动器追加语言参数(关键)

把启动器第 4 步的 Start-Process 改成:

Start-Process -FilePath $exe -ArgumentList @(('--proxy-server=' + $Proxy), '--lang=zh-CN')

--lang=zh-CN 是 Chromium / Electron 级别的开关,菜单栏语言跟着它走。只加这一个参数,重启后菜单栏就是中文。

做法二:配置文件也写死(防止应用内文案回退)

在 ~/.codex/config.toml 里加一行:

[desktop]
localeOverride = "zh-CN"

[desktop] 段里通常已经有 followUpQueueMode / conversationDetailMode,把 localeOverride 加在同一段即可,不要再写第二个 [desktop]。

两条一起用才稳:

  • 只加 --lang:菜单栏中文,但应用内文案仍可能按自动探测走英文
  • 只写 localeOverride:应用内文案可能中文,菜单栏仍可能英文(实测如此)
  • 两条都写:菜单栏与界面正文统一中文

验证:完全退出 Codex(托盘图标也要退),用代理快捷方式重新启动,菜单栏应为 文件 编辑 视图 帮助,左侧栏为 新聊天 / 项目。

补充说明:

  • 26.1016.8622.0 起设置里多了 Settings → General → Language,选「简体中文」等效于写 localeOverride,但不会改启动参数,所以启动器里的 --lang=zh-CN 仍建议保留
  • 应用内文案是否加载还受服务端开关(Statsig 的 enable_i18n)影响。启动时那一次请求超时,正文可能回退英文,菜单栏不受影响;遇到「菜单中文、正文英文」,重启一次让它重新拉取即可
  • 想换回英文:删掉启动器里的 --lang=zh-CN,并把 localeOverride 改成 en-US(或直接删掉该行)

十、回滚

  • 停内核:Get-Process mihomo-codex | Stop-Process -Force
  • 删掉那两个快捷方式,Codex 就回到原样(磁贴启动、走直连)
  • 全程没有改过系统代理,也没有写过用户环境变量,所以不需要额外清理

相关


本文整理自 2026 年 10 月 Windows 11 上的实操验证(Codex 版本 26.1016.8622.0,mihomo v1.19.32)。文中节点、域名、凭据均为示意值,请替换为自己的。整理录入:2026-10-23。

打印/导出
QR Code
QR Code tmpcodexcheck (generated for current page)