上篇《从 VMess+WS+TLS 到 VLESS+Reality 的代理升级记录》记录了旧的进阶方案。这次记录的是更折腾的一段:VLESS+Reality 在自家两条云服务器上被证明完全不可用后,最终迁移到 VLESS+WS 双跳中继(客户端 → 北京中转 → 美国出口)的全过程,以及过程中踩的几个很经典的坑。
背景与目标
家里北京移动网络 → 北京腾讯云中转(154.8.227.217)→ 美国腾讯云出口(43.135.136.107)。验收标准两条:
- Clash Verge(mihomo 内核,rule 模式,mixed-port 7897)下
https://www.gstatic.com/generate_204返回 204,延迟尽可能低; - 出口 IP 必须是 43.135.136.107(美国服务器)。
坑一:怎么连都连不上,"认证失败"
服务器端(V2Ray→换成了 Xray v26.3.27)、密钥对、shortId 全部按官方文档配置无误,可客户端一上来就报 REALITY authentication failed。换客户端内核也无效:正式版 Xray 客户端 26.3.27、V2Ray 5.44.1、Clash/mihomo(v1.17.0 ~ v1.19.x 都试过)、v2rayN 全灭。当时怀疑是内核版本、client fingerprint、dest 域名、甚至 key 对错了——全部排查过,无果。
坑二:global 模式"看似成功",其实是假象
折腾途中发现一个诡异现象:把 Clash 切成 global 模式后,gstatic 居然能返回 204,当时差点当成"成功"收工。还好顺手查了出口 IP——结果是空的。原因链条:
- 节点不可用时,mihomo 会静默直连,而不是报错给你看;
- 直连
www.gstatic.com命中了国内的移动 CDN(120.253.253.98),直接返回 204; - 而
api.ipify.org的 Cloudflare IP 在国内被墙,直连请求被拒,所以出口 IP 为空。
lsof 看连接表一目了然:全局模式下唯一的 TCP 连接是到 120.253.253.98:443,北京中转服务器上一条日志都没有。教训第一条:「能通」不等于「走了代理」,验收必须看出口 IP。
决定性实验:本地回环也连不上
怀疑到最后,把"网络"这个变量彻底从实验里去掉——在 Mac 上跑本地 Xray 服务器(REALITY,loopback),再拿官方 Xray 客户端去连它:仍然失败。服务端开 debug 日志后看到了最关键的证据:
REALITY AuthKey[:16] ... AEAD: *gcm.GCM
ClientVer: [26 3 27]
ClientTime: valid now
ClientShortId: [161 178 195 212 0 0 0 0]
conn == conn: true, isHandshakeComplete.Load(): false
conn: rejected, err: auth ... handshake never completed
认证解密完全成功(shortId、时间都验过了),但 TLS 1.3 的握手永远不完结。换 Xray 1.8.24 官方客户端与服务端同样失败。结论很明确:这个环境下 Xray 的 REALITY 握手本身不可用,和客户端、网络、密钥、域名全都没关系。这条路被判死,放弃。
迁移方案:VLESS+WebSocket
换用最朴素的 VLESS+WS(不带 TLS,因为完整 TLS 动静更大、也需要证书)。先本地回环验证:本地 Xray v26.3.27 起一个 WS 入站,mihomo 用 vless+ws 节点去连,gstatic 204、apple 200,全过。验证通过后再推到两台云服务器。
坑三:换端口被云安全组静默拦掉
新 WS 入站按习惯放到了 8445,结果实网一测就是经典的 dial tcp ...:8445: i/o timeout。用 nc 快速探测一下端口:8444、80、443 通,8445 没反应。腾讯云安全组只放行了部分端口,8445 不在其中。之前 REALITY 用的 8444 是放行的。最省事的办法:WS 直接复用 8444,把两台服务器上已经判死刑的 REALITY 入站删掉。(顺带试过的 7000 北京通、美国不通,别用。)
最终架构与部署
家 (Clash Verge / mihomo)
└─ VLESS+WS ──> 北京 154.8.227.217:8444 (path=/api)
└─ VLESS+WS 出站 us ──> 美国 43.135.136.107:8444 (path=/api)
└─ freedom 直连出网,出口 IP = 43.135.136.107
两端都是 Xray v26.3.27,入站关键配置(北京 / 美国对称):
{
"protocol": "vless",
"settings": { "clients": [ { "id": "<uuid>" } ], "decryption": "none" },
"streamSettings": {
"network": "ws",
"wsSettings": { "path": "/api", "headers": { "Host": "ws.bing.com" } }
}
}
北京这台的出站 us 指到美国:VLESS+WS,uuid 用美国的,wsSettings 同款。北京日志级别从调试期改回 warning。改动前都备份了 config.json,方便回滚。
验收与收尾
gstatic(rule): HTTP 204 (0.87s)
exit ip: 43.135.136.107
apple: HTTP 200
真实 Clash Verge 上把 profile 里的 bj-relay 节点改成 VLESS+WS,通过 unix socket 热重载(PUT /configs?force=true)后,7897 端口同一套验收全过。这里有个细节:Clash Verge 的运行时配置(clash-verge-check.yaml)是"Generated",只改 profile 源文件不会自动生效,要手动改运行时配置后再强制重载,或者干脆重启应用。
经验总结
- 验收看出口 IP,别只看 204。直连也可能碰巧 204,会被假成功骗过去。
- 服务端问题先在本地回环复现。把网络/云防火墙/运营商全部排除后,怀疑对象立刻收敛到协议本身。
- 云服务器换端口前先探安全组。nc -w 1 -z 批量探测被过滤端口通常表现为挂起而不是拒绝,一次探一批。
- 不要迷信协议的"优雅"。REALITY 确实伪装得漂亮,但在这台环境里它连官方客户端都无法完成握手;WS 朴素但稳定,先用起来比什么都强。死磕一个连官方工具都跑不通的协议,时间成本高得离谱。
- 留备份、留版本。两台服务器配置改动前都 cp 了一份,二进制也从 v1.8.24 恢复回 v26.3.27,两端版本保持一致,排障时少一个变量。
写下来算是一个阶段性记录。如果看到这篇文章时 Reality 已经修好,欢迎用回它;但在那之前,VLESS+WS 双跳是这个环境里反复验证过的、稳定可用的方案。
文章评论