租一台国外服务器,就像在地球另一边买了一小块地。一开始只想盖个小网站,盖着盖着,又顺手修了条”私人隧道”,最后还给 Claude 盖了间”笼子”。这篇就来聊聊这块地上都盖了些什么,以及它们是怎么连起来的。

先放全景图:

【AWS Lightsail · 俄亥俄 · Windows Server 2022】

浏览器 ──HTTPS 443──▶ Caddy ──反代──▶ Intel-Eye (127.0.0.1:8080)
│ └──▶ PostgreSQL / Redis
│
└─ Let's Encrypt 证书(顺手借给 sing-box 用)
│
iPad / Clash / Cage ──UDP 8443 (Hysteria2)──▶ sing-box ──▶ 外网
└─TCP 8443 (Reality, 备用)──┘

一台机器,三件事:一个反向代理网站、一条自用的科学上网通道、给 Claude Cage 当网络出口。下面一件一件说。

零、先把地基打好

组件选择一句话理由
服务器AWS Lightsail,us-east-2(俄亥俄),Windows Server 2022按月付费、带固定公网 IP,控制台自带防火墙
域名DuckDNS 免费二级域名 xxx.duckdns.org不花钱。缺点是得隔段时间登录一下保活
HTTPSCaddy写一行域名,证书自动申请、自动续期
后台服务WinSW把任意 exe 包装成 Windows 服务,开机自启、挂了自动拉起

对外开放的端口只留最少的几个:

端口协议用途
22TCPSSH 远程管理
80TCP给 Let’s Encrypt 验证域名用,不能关,关了证书续不上
443TCP网站 HTTPS
8443TCP + UDP代理入口(TCP 给 Reality,UDP 给 Hysteria2)

小坑预警:云服务器的防火墙有两层——Lightsail 控制台一层,Windows 自己的防火墙一层。只开一层等于没开,排查时请先默念三遍”两层两层两层”。

一、反向代理网站:Intel-Eye

Intel-Eye 首页

Intel-Eye 是我基于开源项目 sub2api 改出来的一个 AI API 网关,后端是 Go,前端是 Vue。界面整个重新设计了一遍,配了一只赛博眼睛当 logo。

请求是怎么走的

浏览器 → https://xxx.duckdns.org (443)
→ Caddy:终结 TLS,转发到本机
→ Intel-Eye 后端,只监听 127.0.0.1:8080,外网根本看不到它
→ PostgreSQL 17 + Redis(同样只在本机)

关键在于只有 Caddy 站在门口。后端、数据库、缓存全都缩在 127.0.0.1 里,外面的人想直接连 8080,门都没有。

Caddyfile:短到有点感人

{
email you@example.com
}

xxx.duckdns.org {
reverse_proxy 127.0.0.1:8080 {
flush_interval -1
}
}

就这么几行,Caddy 会自己:

  1. 去 Let’s Encrypt 申请证书(所以 80 端口得开着);
  2. 到期前自动续期;
  3. 把 HTTP 自动跳转到 HTTPS。

这里有一个很容易忽略的细节:flush_interval -1。大模型接口是流式输出(SSE),一个字一个字往外吐。如果反代默认把响应攒一攒再发,用户就会看到”卡了半天,突然一大段字砸下来”。设成 -1 就是告诉 Caddy:来一点转一点,别攒。

发版流程

前端打包进后端,编译成一个 exe,传上去,换文件,重启服务,查健康状态:

# 本地:先构建前端,再用 -tags embed 把前端页面一起打进 Go 二进制
cd frontend; pnpm run build; cd ../backend
$env:CGO_ENABLED='0'
go build -tags embed -trimpath -ldflags "-s -w" -o bin/server-intel-eye.exe ./cmd/server

# 传到服务器,停服务,换文件,再启动
scp bin/server-intel-eye.exe aws:C:/sub2api/app/sub2api-new.exe
ssh aws 'Stop-Service sub2api -Force; Move-Item C:\sub2api\app\sub2api-new.exe C:\sub2api\app\sub2api.exe -Force; Start-Service sub2api'

# 确认活着
ssh aws '(Invoke-WebRequest http://127.0.0.1:8080/health -UseBasicParsing).StatusCode' # 200 就对了

每次替换前把旧的 exe 备份一下,万一新版翻车,换回去就是一行命令的事。

二、重头戏:用 Hysteria2 修一条私人隧道

网站搭好后我发现:这台机器 24 小时开着、有固定 IP、本来就在海外……不顺手当个梯子,简直对不起它的月租。

两位选手

我在同一个端口 8443 上同时放了两种协议。TCP 和 UDP 是两套独立的端口空间,所以它们可以和平共处:

VLESS + RealityHysteria2
传输层TCPUDP(QUIC)
伪装方式握手时冒充访问 www.microsoft.com看起来就是一个正常的 HTTP/3 网站
证书不需要需要一张真证书
我这里的实测速度约 64 KB/s 🐢约 1.6 MB/s 🚀

差了二十多倍,胜负一目了然。

为什么 Hysteria2 能跑这么快

跨太平洋的线路,最大的敌人是丢包。

TCP 的性格是”老实人”:一发现丢包,就以为网络堵了,立刻自觉降速。跨洋线路上随手丢个包太正常了,于是 TCP 一路降、一路降,最后慢得像在拨号上网。

Hysteria2 跑在 QUIC(UDP)上,拥塞控制可以自己说了算。可以给它填上带宽,让它用 Brutal 算法”不管丢不丢包,就按这个速度发”;不填的话用 BBR,看的是实际带宽和延迟,而不是”丢没丢包”。在丢包多的线路上,这两种思路都比传统 TCP 硬气得多。

而它的伪装也很聪明:QUIC 本来就是 HTTP/3 的底层协议,现在大量正常网站都在用。只要再配上一张真实有效的证书,它看起来就和一个普通的 HTTP/3 网站没什么两样。

精髓:证书直接”借”隔壁 Caddy 的

Hysteria2 要证书,而 Caddy 刚好已经为我的域名申请好了一张,还会自动续期。那还申请什么,直接拿来用!

Caddy 在 Windows 上会把证书放在它的数据目录里,大概长这样:

C:\caddy\data\caddy\certificates\acme-v02.api.letsencrypt.org-directory\xxx.duckdns.org\
xxx.duckdns.org.crt
xxx.duckdns.org.key

让 sing-box 直接读这两个文件就行。一张证书,网站和隧道两头共用。

动手:五步搭起来

第 1 步:下载 sing-box

从 sing-box 的 GitHub Releases 下载 windows-amd64 版本,解压出 sing-box.exe。

第 2 步:生成密钥

sing-box 自带生成器,不用自己瞎编:

.\sing-box.exe generate rand 16 --hex      # Hysteria2 的密码
.\sing-box.exe generate uuid # Reality 用户的 UUID
.\sing-box.exe generate reality-keypair # Reality 的公私钥
.\sing-box.exe generate rand 8 --hex # Reality 的 short_id

第 3 步:写配置 config.json

下面是精简后的配置,尖括号里的内容换成你自己的:

{
"log": { "level": "warn", "timestamp": true },
"inbounds": [
{
"type": "hysteria2",
"tag": "hy2",
"listen": "::",
"listen_port": 8443,
"users": [{ "name": "ipad", "password": "<HY2 密码>" }],
"tls": {
"enabled": true,
"server_name": "xxx.duckdns.org",
"certificate_path": "<Caddy 证书目录>\\xxx.duckdns.org.crt",
"key_path": "<Caddy 证书目录>\\xxx.duckdns.org.key"
}
},
{
"type": "vless",
"tag": "vless-reality",
"listen": "::",
"listen_port": 8443,
"users": [{ "name": "ipad", "uuid": "<UUID>", "flow": "xtls-rprx-vision" }],
"tls": {
"enabled": true,
"server_name": "www.microsoft.com",
"reality": {
"enabled": true,
"handshake": { "server": "www.microsoft.com", "server_port": 443 },
"private_key": "<Reality 私钥>",
"short_id": ["<short_id>"]
}
}
}
],
"outbounds": [{ "type": "direct", "tag": "direct" }]
}

写完先体检一下:

.\sing-box.exe check -c config.json   # 没输出就是没问题

第 4 步:注册成 Windows 服务

继续请 WinSW 出场。把 WinSW.exe 复制一份改名叫 proxy-lab-service.exe,旁边放一个同名的 proxy-lab-service.xml:

<service>
<id>proxy-lab</id>
<name>proxy-lab</name>
<description>sing-box (Hysteria2 UDP 8443 + VLESS-Reality TCP 8443)</description>
<executable>C:\proxy-lab\sing-box.exe</executable>
<arguments>run -c C:\proxy-lab\config.json -D C:\proxy-lab</arguments>
<log mode="roll-by-size"><sizeThreshold>10240</sizeThreshold><keepFiles>3</keepFiles></log>
<onfailure action="restart" delay="5 sec"/>
<startmode>Automatic</startmode>
</service>

然后执行 .\proxy-lab-service.exe install,再 Start-Service proxy-lab。从此开机自启,挂了 5 秒后自动拉起。

第 5 步:开门(两层!)

# Windows 防火墙
New-NetFirewallRule -DisplayName "proxy-lab 8443 TCP" -Direction Inbound -Protocol TCP -LocalPort 8443 -Action Allow
New-NetFirewallRule -DisplayName "proxy-lab 8443 UDP" -Direction Inbound -Protocol UDP -LocalPort 8443 -Action Allow

然后去 Lightsail 控制台的”网络”页,把 TCP 8443 和 UDP 8443 都加上。很多人只记得开 TCP,结果 Hysteria2 死活连不上,这个坑我替你们踩过了。

客户端怎么连

分享链接(iPad 上的客户端扫二维码或粘贴都行):

hysteria2://<HY2 密码>@<服务器 IP>:8443?sni=xxx.duckdns.org#My-Hy2

Clash / mihomo 配置(电脑上的 Clash Verge Rev 用这个):

proxies:
- name: My-Hy2
type: hysteria2
server: <服务器 IP>
port: 8443
password: <HY2 密码>
sni: xxx.duckdns.org

proxy-groups:
- name: Proxy
type: select
proxies: [My-Hy2, DIRECT]

rules:
- GEOSITE,cn,DIRECT
- GEOIP,CN,DIRECT
- MATCH,Proxy

国内网站直连,其他走隧道,各走各的路。

验一下货:

curl.exe -x http://127.0.0.1:7897 https://api.ipify.org                                # 应该返回服务器的 IP
curl.exe -x http://127.0.0.1:7897 -o NUL -w "%{http_code} %{time_total}s" https://www.google.com/generate_204 # 204 就通了

维护小贴士

  • 证书续期后要重启服务。 Caddy 续完证书,sing-box 并不会自己重新读文件,记得 Restart-Service proxy-lab。懒的话可以加个每月跑一次的计划任务。
  • 80 端口别关。 它是证书续期的生命线。
  • DuckDNS 要偶尔登录一下,不然域名可能被回收。
  • 密码、UUID、私钥都只存在服务器和自己电脑上,千万别连同配置一起传到 GitHub。

三、给 Claude 盖一间笼子:Claude Cage

Claude Cage 工作台

用 Claude 的朋友大概都懂那种提心吊胆:时区对不上、语言对不上、IP 漂来漂去,每一样都可能让账号被风控。Claude Cage 这个开源项目的思路很直接:别让 Claude 看到你的真实电脑,给它一台专属的隔离虚拟机。

它做了这几件事:

  • 网络强制走代理:VM 里的所有流量都由 Cage 内置的 mihomo 接管,代理不通就直接断网,不会偷偷直连;
  • 指纹跟着出口走:时区、语言可以按代理 IP 自动同步,人在国内,环境”住”在美国;
  • 启动前先体检:网络与隔离检查不通过,外网就不放行;
  • 一个窗口全搞定:终端里跑 Claude Code,内置浏览器负责登录授权,还可以按需装 Claude Desktop。

和我的服务器怎么连起来

Cage 不走电脑上的 Clash,它有自己的 mihomo 核心。所以只要把上面那段 Clash YAML 导入 Cage 的”本地 YAML 来源”,选中 Hy2 节点,点”应用代理”就行了。

于是链路变成了:

Cage 虚拟机里的 Claude Code / 浏览器
→ Cage 内置的 mihomo
→ Hysteria2 隧道
→ 我的 AWS 服务器
→ Anthropic

对 Anthropic 来说,访问者就是一台稳定的美国机器,IP 固定、时区一致,干干净净。我一开始用的是 Reality 节点,环境初始化慢得让人怀疑人生;换成 Hy2 之后,速度立刻就上来了。

在 Windows 上踩过的坑

Cage 的 Windows 版还在预览阶段,我这边一路踩过来,记一下给后来人:

  1. 先开虚拟化功能:在”启用或关闭 Windows 功能”里勾上 Windows 虚拟机监控程序平台(HypervisorPlatform)和虚拟机平台(VirtualMachinePlatform),然后重启。
  2. QEMU 的 WHPX 加速报错:遇到 WHPX: Failed to ... hr=80370302 之类的错误,可以设置一个用户环境变量,关掉内核中断控制器加速来绕开它:
    QEMU_SYSTEM_X86_64 = qemu-system-x86_64 -machine q35,accel=whpx,kernel-irqchip=off
  3. 重启后卡在”无法连接 VM 外网络门槛控制器”:这个最有意思。Cage 会把后台进程的 PID 记在 cage-running.json 里,重启后这个 PID 刚好被系统的 svchost.exe 捡走了。Cage 一看”PID 还活着”,就以为 VM 还在运行,于是不启动也连不上,就这么僵住了。临时解决办法是把这个文件改个名再点”启动 Cage 环境”。

第 3 个问题我顺手修了:让运行记录多存一个进程启动时间,PID 相同但启动时间对不上,就说明这个 PID 已经被别人捡走了。修复已经提交给上游:issue #12、PR #13。给开源项目递 PR 的感觉,就像在别人家门口顺手扫了个雪,还挺开心的。

四、总结:这块地值不值

回头一看,一台月租不贵的小服务器,干了三份工:

  1. 对外:用 Caddy 反代出一个带 HTTPS 的正经网站;
  2. 对己:用 sing-box + Hysteria2 修了一条又快又稳的私人隧道,手机、平板、电脑都能用;
  3. 对 Claude:给 Claude Cage 提供一个固定、干净的出口,让工作环境稳稳地”住”在海外。

其中最让我觉得巧妙的,是一张证书两头用:Caddy 负责申请和续期,sing-box 白嫖,网站和隧道共享同一个域名身份。整套东西的核心配置加起来不过百来行,但每一行背后都有点讲究。

如果你手里也有一台闲着的海外服务器,不妨试试。(当然,请在当地法律法规允许的范围内折腾。)