一行响应头

Nieo Lv.8

终端里的 Claude Code 登不上。浏览器给出的答案是「App unavailable in region」——看上去是个再清楚不过的翻墙问题。

aca75a1c148fab6bc355a525da9897ab

它不是。真正的答案写在一行几乎没人会去看的响应头里,中间隔着四层互相掩盖的故障。

第一层:一把作废的钥匙

别看浏览器,先让 CLI 自己说话:

1
claude -p "reply with exactly: OK"
1
Failed to authenticate. API Error: 401 OAuth access token has been revoked.

Keychain 里躺着一份被吊销的 OAuth token,客户端只会拿它去刷新,刷不动就永远卡在这儿。重新登录即可——如果登得上的话。

第二层:七千条规则里的一个洞

重新登录,授权页正常,点下 Authorize,跳转,地区拦截。

去翻 Clash 的规则表:

1
grep -niE 'claude|anthropic' clash-verge.yaml
1
8870:- DOMAIN-SUFFIX,anthropic.com,GPT

七千多条规则,claude.ai 和 claude.com 一条都没有。两个域名一路穿过整张表,落进最后的 MATCH,Others——而那个组的候选里躺着 DIRECT。

前后一下就对上了。api.anthropic.com 有规则,走代理,所以 CLI 能连上 API,报的是干净的 401 而不是超时;claude.com 没规则,直连出门,撞上地区墙。

补四条前置规则,确认它们进了生成配置的最前面。

问题依旧。

两条弯路

第一条:我怀疑浏览器缓存。 用另一个浏览器实测 claude.com,正常。同一台机器、同一个出口,两个浏览器两种结果——于是我断定是浏览器缓存了修复前那张拦截页。

错。这个判断建立在「两个浏览器走同一条路」这个从未验证过的假设上。更糟的是,我用来支撑它的证据——我的浏览器能打开——本身就是那个待解释的现象,不是解释。

第二条:我怀疑 IPv6 泄漏。 浏览器优先 IPv6、而 fake-ip 只覆盖 IPv4,这是个常见的漏法。两条命令:

1
2
ifconfig | grep 'inet6' | grep -v 'fe80\|::1'   # 空
dig +short AAAA claude.com # 空

本机没有全局 IPv6,域名也没有 AAAA 记录。排除,收工。

两条弯路的差别值得记一下:第二条是被证据干净否掉的,成本两条命令;第一条是我拿猜测当了结论,还基于它给出了一轮操作建议。

转折:去要终端里的那行字

我一直在分析浏览器截图。而真正的报错在终端里躺着:

1
OAuth error: Request failed with status code 403

同时拿到了另一个浏览器的出口 IP——和我测到的一模一样。

弯路一当场作废。浏览器之间根本没有路径差异,差异在别的地方。

一行响应头

不要只看状态码。看头:

1
curl -s -D - -o /dev/null https://claude.ai/api/hello
1
2
3
HTTP/2 403
cf-mitigated: challenge
server: cloudflare

响应体是 <title>Just a moment...</title>。Cloudflare 的托管质询。

补上完整的浏览器请求头再试——UA、Accept、Accept-Language、Sec-Fetch-Mode,一个不落。照样 403。拦的不是 User-Agent,是 IP 本身。

机制到这儿就全亮了:

浏览器会执行 JS,能把质询解掉,所以网页打得开。CLI 用的是 Node 的 HTTP 客户端,不执行 JS,解不了,直接吃 403。

同一个 IP,两种结局。不是「路不同」,是能不能解题不同。而我前面所有的推理,都默认了浏览器能打开就等于路是通的。

可是,哪个端点?

有意思的是,并非所有端点都被拦:

1
2
3
POST console.anthropic.com/v1/oauth/token  →  400   (正常的参数错误)
POST claude.ai/v1/oauth/token → 400
GET claude.ai/ → 403 质询

质询只下发给页面路由,/v1/oauth/* 是放行的。那 CLI 的 403 到底撞在哪个请求上?

不猜。原生安装是个单文件可执行程序,直接挖:

1
2
3
strings ~/.local/share/claude/versions/<ver> \
| grep -oE 'https://[a-z0-9.-]*(anthropic|claude)[a-z0-9.-]*/[A-Za-z0-9/_.-]*(oauth|token)[A-Za-z0-9/_.-]*' \
| sort -u
1
2
3
4
https://api.anthropic.com/api/oauth/claude_cli/roles
https://claude.com/cai/oauth/authorize
https://platform.claude.com/oauth/code/success
https://platform.claude.com/v1/oauth/token

platform.claude.com。既不是 console,也不是 claude.ai——难怪我前面测的两个 token 端点都是好的,我压根没在测它真正请求的那个域名。

验证,一击命中:

1
2
POST platform.claude.com/v1/oauth/token   →  403
GET api.anthropic.com/.../roles → 401 (正常未认证)

换节点为什么没用

切到另一个国家的节点,还是 403。查出口归属才发现,两个节点分属不同国家,出口却落在同一家 IP 供应商手里。机场的主力线路共用一个被风控标记的池子,换国家是没有意义的,得换供应商。

手工试遍所有节点显然不现实。看内核启动参数:

1
2
ps -o command -p $(pgrep verge-mihomo)
# verge-mihomo -d <dir> -f <config> -ext-ctl-unix /tmp/verge/verge-mihomo.sock

有 Unix socket 控制接口。那就不用手了——切换、打端点、记状态码,写成一个循环:

1
2
3
4
5
6
7
8
9
SOCK=/tmp/verge/verge-mihomo.sock
while IFS= read -r node; do
curl -s --unix-socket "$SOCK" -X PUT -H 'Content-Type: application/json' \
-d "{\"name\":\"$node\"}" http://localhost/proxies/GPT >/dev/null
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 \
-X POST -H 'Content-Type: application/json' -d '{}' \
https://platform.claude.com/v1/oauth/token)
printf "%-4s %s\n" "${code:-ERR}" "$node"
done < nodes.txt

结果干净得不像话:

  • 403 —— 日本、美国、新加坡、台湾、香港,全部主力线路,一个不剩
  • 400 —— 土耳其、埃及、阿联酋、尼日利亚、印度、南非、巴西、阿根廷

热门地区的 IP 被薅得太狠,风控画像早就花了;冷门地区反而干净。Cloudflare 的质询有概率成分,交替跑了三轮,结果稳定复现。

切到干净节点,token 端点返回的是 Anthropic 真实的 API 错误——带 request_id 的那种。放行了。

只分流一个域名

再测一遍登录链路上的全部端点:在快速节点上,只有 platform.claude.com 被拦,其余一律正常。

那就没必要把所有流量绕到地球另一端。只分流这一个域名:

域名走哪为什么
platform.claude.com干净节点组只有它被拦
api.anthropic.com主力节点推理流量,延迟敏感
claude.ai / claude.com主力节点浏览器自己能解质询

组用 url-test + include-all-proxies + 关键词过滤,自动收纳那批干净节点,按延迟挑最快的,挂了自动切——订阅更新换了节点名也不容易失效:

1
2
3
4
5
6
- name: Claude-OAuth
type: url-test
include-all-proxies: true
filter: "(印度|巴西|土耳其|阿联酋|埃及|南非|尼日利亚|阿根廷)"
url: http://www.gstatic.com/generate_204
interval: 300
1
2
3
4
- DOMAIN,platform.claude.com,Claude-OAuth
- DOMAIN-SUFFIX,claude.ai,GPT
- DOMAIN-SUFFIX,claude.com,GPT
- DOMAIN-SUFFIX,anthropic.com,GPT

落盘前先用内核离线校验,别把配置写坏:

1
verge-mihomo -t -d <config-dir> -f test-config.yaml
  • Title: 一行响应头
  • Author: Nieo
  • Created at : 2026-09-01 13:20:00
  • Updated at : 2026-09-18 17:47:06
  • Link: https://gme-hong.github.io/2026/09/01/Claude-Code-Login-Cloudflare/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments