为什么三个入口的出口可以不一样
这是整个工具存在的理由,值得单独一份文档。
一、根源:三个入口读的不是同一份代理配置
| 入口 | 读什么 | 匹配依据 |
|---|---|---|
| Claude Code(CLI,Node 运行时) | 只认 HTTPS_PROXY / HTTP_PROXY 环境变量。没设就直连 | 直连时如果本机有 TUN / 透明代理,分流器按 IP 匹配规则 |
| Claude 桌面端(Electron / Chromium) | 认操作系统的代理设置 | 通过 HTTP CONNECT 把域名交给代理,分流器按域名匹配规则 |
| 浏览器里的 claude.ai | 认系统代理,除非装了代理插件自己接管 | 同上;插件可以单独改写 |
Node 默认不读系统代理。这一条是所有困惑的起点: 你在系统设置里配好了代理,桌面端立刻生效,而 Claude Code 完全不知道它的存在。
"按 IP 匹配"和"按域名匹配"是两个不同的匹配过程,命中的规则可以不同。 于是同一个域名,从 CLI 出去和从桌面端出去,落地国家可以不一样。
二、这个工具怎么测
对每个入口造一条等价探测路径:用和该入口完全相同的代理配置, 去问目的地的 cdn-cgi/trace(见 01-endpoints.md 第三节)。 目的地返回的 ip= 就是该入口真实的出口地址 —— 不是本机接口地址, 也不是猜的。
- CLI 路径:按
HTTPS_PROXY是否存在,走代理或直连; - 桌面端 / 网页路径:走系统代理(读
scutil --proxy), 用真正的 HTTPCONNECT建隧道,DNS 交给代理做 —— 和 Chromium 一样; - 对照路径:强制不用任何代理。
python3 -m cem doctor # 先看清本机有哪几条路径
python3 -m cem probe # 再量每条路径的出口
系统代理是 PAC 脚本时
PAC(自动配置脚本)对每个 URL 可以返回不同的代理,不存在"一个"代理。 本工具检测到 PAC 时退回直连并在输出里说明,结论仅供参考 —— 硬凑一个数字比说不知道更糟。
三、三个会让人得出错误结论的陷阱
陷阱一:fake-ip —— dig 查出来的地址是假的
开了 TUN 模式的分流器(Clash / mihomo / sing-box)通常同时开 fake-ip: 它拦下 DNS 查询,给每个域名返回一个 198.18.x.x 的占位地址, 自己记住"这个占位地址对应哪个域名",等程序连上来时再按域名匹配规则。
后果:
dig +short api.anthropic.com
# 198.18.0.56 ← 这不是任何真实主机
- 查这个地址的 ASN / 归属毫无意义;
lsof里看到进程连198.18.0.140也不代表它连了什么可疑的东西。
198.18.0.0/15 是 RFC 2544 给网络设备基准测试保留的段,公网不可路由 —— 所以拿来做占位很安全,也正因如此,它出现在你的连接表里一定是本地分流器造成的。
这个坑反过来有一个好处:分流器给每个域名分配独立的占位地址, 所以可以反查 —— 看到 198.18.0.140 就能推出它是哪个域名。 在正常环境里做不到这件事(多个域名共用一个 CDN IP)。 本仓的 cem/resolve.py 就是用这个办法给连接表补上域名。
要拿到真实解析,得走 DoH:报文是端到端加密的,中间的分流器改不了里面的答案。
fake-ip 会不会让「出口 IP」也测不准?
不会。 这是这一节最容易读岔的一句,值得单独说清楚。
| 这个工具的测量项 | 怎么拿到的 | fake-ip 有影响吗 |
|---|---|---|
| 出口 IP / 落地国家 / 边缘机房 | 对域名建一条真的 HTTPS 连接,问目的地 cdn-cgi/trace | 没有 |
| DNS / TCP / TLS / 首字节 四段延迟 | 同一条连接上分段计时 | 没有 |
| 逐跳延迟与丢包(路径质量) | 往一个具体 IP 发 ICMP 探测包 | 有,而且是致命的 |
区别在于问的对象不同:
- 前两项是问目的地。占位地址只是本机内部的一个标签,程序连上去之后 分流器照样把这条连接转发出去,Cloudflare 那侧看到的就是你真实的出口地址, 它把这个值原样告诉你。这条问答走的是真实链路,fake-ip 拦不住。
- 路径质量是往一个 IP 号码上打包。而这个号码是假的、公网上不存在, 包发出去就被本机分流器接住了 —— 得到的是"到本机分流器"的一跳, 不是"到 Claude"的路径。
所以在一台全程 fake-ip 的机器上,界面表现是:出口 IP、地区、colo、四段延迟 全部正常显示,只有「路径质量」那一屏说"测不了",并给出三种绕开的办法。 这不是工具瞎了,是那一项的测量前提被破坏了,而它选择说出来而不是给个假数字。
顺带一提,这也解释了为什么本工具不靠 dig 判断出口: dig 拿到的是本机解析结果(可能是占位地址),而"目的地看到的是谁"只能问目的地。
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=api.anthropic.com&type=A'
界面上的「解析对账」面板就是把这两个结果并排放,不一致就标出来。
陷阱二:按域名分派的解析器(split-DNS)
macOS 支持给不同域名指定不同的 DNS 服务器。VPN、Tailscale MagicDNS、 企业配置都会往里写。
scutil --dns | grep -A2 -E '^\s*domain'
如果输出里出现类似
domain : api.anthropic.com
nameserver[0] : <某个隧道内的地址>
说明这个域名的解析被交给了另一条隧道 —— 而解析走哪条隧道,流量通常也走那条。 这就解释了一类极难排查的现象:同一台机器上,某几个域名的行为和其他域名完全不同。
cem doctor 会把命中的域名和对应的 nameserver 都列出来。
陷阱三:lsof 看不到代理后面的目的地
走本机 HTTP 代理的进程,lsof 只能看到
127.0.0.1:52044 -> 127.0.0.1:<代理端口>
真实目的地在代理进程内部。桌面端和浏览器正好属于这一类, 所以它们的连接表天生是不完整的。
工具不会假装这条限制不存在:这类连接标成 local-proxy, 并在结论里写明有多少条目的地不可见。
四、补上第三个陷阱:读分流器自己的连接表
Clash / mihomo(含 Clash Verge、各种 GUI 前端)以及兼容 clash-api 的 sing-box 都提供一个控制接口。开了之后:
curl -s http://127.0.0.1:9090/connections | python3 -m json.tool | head -40
每条连接会带上这些字段,正是 lsof 缺的那一半:
| 字段 | 含义 |
|---|---|
metadata.host | 真实目标域名(不是占位地址) |
metadata.processPath | 哪个进程发起的 |
rule / rulePayload | 命中了哪条分流规则 |
chains | 走了哪条代理链,第一个元素就是最终出口节点 |
upload / download | 这条连接的流量 |
怎么打开:在配置文件里加
external-controller: 127.0.0.1:9090
secret: "换成一个你自己的随机字符串"
安全注意:
- 地址只写
127.0.0.1。绑到0.0.0.0等于把"改我全部代理规则"的权限 开给局域网。 - 一定设
secret。没有 secret 的控制接口,本机任何进程(包括网页里的 JavaScript,如果它能访问回环)都能改你的配置。 - 本仓只读
/connections,从不写任何配置。secret 只从命令行参数或 环境变量读,不落盘。
cem/clash.py 是这部分的实现,接了之后连接表里的 local-proxy 行就能 换成真实域名和出口节点名。
五、把配置改成一致的
量出来不一致之后,通常有三种改法:
改法一:让 CLI 也走系统代理那条路。 在 shell 配置里显式设:
export HTTPS_PROXY=http://127.0.0.1:<你的代理端口>
export HTTP_PROXY=http://127.0.0.1:<你的代理端口>
export NO_PROXY=localhost,127.0.0.1,::1
设完再跑一次 cem probe,两个入口的出口应当一致。
改法二:让分流规则对 Claude 的所有域名给同一个出口。 在分流器里给这一组域名建一个规则集合,指向同一个节点组。 这样按 IP 命中还是按域名命中都落到同一个出口。 清单可以直接从 python3 -m cem endpoints --json 生成。
改法三:什么都不改,但知道自己在做什么。 有时候不一致是有意的 (业务走低延迟节点、更新下载走另一条)。这时候监控的价值是 在它意外变化时告诉你 —— 界面上的「出口变更」时间轴就是为这个存在的。
六、受限地区
Claude 的服务条款把若干地区列为受限。出口落在这些地区是账号风险, 不是"慢一点":
CN 中国大陆 · HK 香港 · MO 澳门 · RU 俄罗斯 · KP 朝鲜 · IR 伊朗 · SY 叙利亚 · CU 古巴 · BY 白俄罗斯 · VE 委内瑞拉
这份名单在 cem/view.py 的 RESTRICTED_CC 里,界面上命中会标红色药丸。
需要说明清楚的是:这份名单是本工具依据公开条款整理的,不是 Anthropic 的 官方接口返回的。它会过期,最终以官方条款为准。工具的职责是 "出口落在这里时提醒你去核对",不是"替官方判定"。