扔台 VPS 给 AI,让它帮我搭梯子
开发者深夜配置代理服务器
用了好几年商业 VPN。用着用着越来越别扭。流量全过别人的服务器,对方是谁、数据怎么处理,全凭信任。
做了这么多服务端的活,自己搭一个按道理不难。
租了一台 VPS。Atlas Networks,洛杉矶,9929 线路,静态住宅 IP。然后把 SSH 凭据扔给 Claude Code:帮我搭一套完整的代理服务,自用也行,分享给朋友也行。
几件事出乎我的意料。失败的方向,和最后搭出来的东西。
Shadowsocks:四小时阵亡
Claude Code 第一步部署的是 Shadowsocks。成熟方案,默认选择。
四个小时之后 IP 就被封了。
不是限速。整个封掉。流量直接断了。联系服务商确认了:主动封锁。
这件事让我搞明白了 GFW 检测的基础原理。不是一个静态黑名单,是一套主动探测系统。看到流量特征不符合已知协议,主动去探你的服务器——发异常报文、测响应模式。Shadowsocks 曾经很难被识别,GFW 对它已经训练了很多年了。
2025 年用一个新 IP 部署 Shadowsocks,基本就是举牌告诉它"来探我"。
问题不是加密。GFW 读不了你的内容。是流量指纹。高熵密文、非标准握手、响应模式匹配代理行为。Shadowsocks 全中。
换台 VPS,换个思路。
Reality:藏在明处的艺术
新方案是 sing-box + VLESS-Reality。
Claude Code 解释 Reality 原理的时候,这设计确实聪明。不是让流量看起来像什么都没有,是让流量看起来像在访问一个真实网站——借用它的 TLS 指纹。你的握手和一个普通浏览器访问 microsoft.com 一模一样。
GFW 要么让你过,要么把微软也封了。永远不会选后者。
Reality 之外还部署了三个备用协议。Hysteria2 和 TUIC-v5,基于 QUIC 的 UDP,速度快但吃网络质量。Vmess-WS,WebSocket,最弱但最后兜底。四个协议,四个端口。防火墙只开了这几个,其他全关。
Clash Verge 的配置里,自动选择分组只包含 Reality。其他协议底层备用,不参与日常路由。Reality 够用就永远走 Reality。
证书踩坑和一路推进
协议选好之后,Claude Code 一路推进。
SSL 证书用的 acme.sh + Cloudflare DNS API 验证。不需要开 80 端口。给 acme.sh 一个只有 DNS 编辑权限的 Cloudflare API Token,创建 TXT 记录验证域名所有权,验完自动清除。很干净,服务器不用暴露额外端口。
中间踩了一个坑。sing-box 一键脚本在内部跑过一次 acme.sh,把域名存进了邮箱字段的缓存里。后来单独给订阅服务器申请证书的时候,acme.sh 复用了那份缓存,Let's Encrypt 收到的证书请求里邮箱字段是个域名格式,直接拒绝了。
错误信息有点绕,三个来回才定位到。删掉 CA 缓存目录,带上 --accountemail 重新跑,才过。
防火墙、BBR 加速、服务开机自启,Claude Code 一并处理了。基本不用我介入。
订阅服务器:最有成就感的部分
很多自建代理方案就是生成一个 base64 字符串,或者一个 QR code,手动导进去。
我想要更完整一点的东西。一个真正的订阅 URL,客户端能读到流量统计。Clash Verge 支持 Subscription-Userinfo 响应头,读到后渲染一个流量条——已用多少、剩余多少、到期时间。这个细节让整套服务看起来完整,不像拼凑出来的。
Claude Code 搭了一个 Python 服务跑在 nginx 后面:nginx 处理 443 端口的 TLS,请求代理到本机 8089。Python 服务读订阅配置文件,同时调 vnstat --json 拿 VPS 的真实上下行数据,动态生成响应头。头格式:upload=X; download=Y; total=Z; expire=T。Clash Verge 拿到后自动渲染,不需要任何客户端配置。
Token 是一段随机十六位 hex,存在服务器上。订阅 URL 就是 https://your.domain.com/{token}。
有个小插曲。最开始 Python 服务没处理 HEAD 请求,Clash Verge 用 HEAD 拿 header 时服务直接报错。加上 do_HEAD 方法就好了。这种细节不跑起来根本发现不了。
朋友那边能看到流量条。已用多少、3TB 月限额剩多少、到期日期。跟商业服务没什么区别。Clash Verge:粘贴 URL,点导入,激活配置,开 TUN 模式。Shadowrocket(iOS):URL 直接添加为订阅,或者扫 QR code。
Claude Code 是怎么处理失败的
回看这个过程,印象最深的不是做了什么,是怎么处理失败。
证书因为缓存问题三次申请失败,没有重复执行同一条命令。去查 acme.sh 的状态,找到那份坏缓存,清掉,重跑。nginx 的 proxy 配置有时候传 header 有时候不传,把整个链路走了一遍,逐层确认每个环节在传什么、缺了什么。
每次失败大概十到十五分钟。没有绕开,没有放弃。一直在找真正的原因。
Clash Verge 本身的行为是个例外。重启时会删掉手动编辑的配置文件,只保留通过订阅 URL 导入的。这个客户端特有的行为,Claude Code 不知道,最终是一起通过测试发现的。
有一条界限很清楚。凡是有明确验证标准的任务——"这个 URL 应该返回包含 proxies: 字段的 YAML"——调试起来很干净,可以一直循环到通为止。凡是客户端行为比较特殊的地方,需要人来观察和判断。
分工明确,效率就高。
一个下午,一套自己托管的代理服务,朋友发个链接就能用。