用 Caddy + FRP + CLIProxyAPI,将本地 AI 服务安全发布到公网

用 Caddy + FRP + CLIProxyAPI,将本地 AI 服务安全发布到公网
Mr.L最近我把本地高性能主机上的 CLIProxyAPI 通过一台有公网 IP 的阿里云 ECS 暴露到了公网,整体方案比“直接开放本地端口”稳妥很多,也更适合长期维护。
这篇文章就按实际落地过程,完整记录一下 Caddy + FRP + CLIProxyAPI 的部署思路和操作步骤。
一、整体目标
我当前有两台机器:
- A:阿里云 ECS
- 有公网 IP
- 负责域名入口、HTTPS 证书、反向代理
- B:本地高性能主机
- 没有公网 IP
- 负责真正运行 CLIProxyAPI
最终链路如下:
1 | 用户 / Claude Code / 浏览器 |
最终对外访问地址:
1 | https://ai.YOUR_DOMAIN |
二、为什么不用直接暴露本地服务
如果把本地服务器直接发布到公网,通常会遇到几个问题:
- 本地机器没有公网 IP
- 家宽、校园网、办公网往往都在 NAT 后面
- HTTPS 证书、域名解析和端口映射维护麻烦
- 直接暴露本地服务,安全边界不好控制
所以更合理的思路是:
- A 负责公网入口
- B 负责实际算力和服务
- 用 FRP 把 B 的本地端口映射到 A
- 用 Caddy 统一处理 HTTPS 和反向代理
这样公网始终只需要暴露 A,B 可以继续只作为内网服务节点存在。
三、最终请求链路
实际请求会这样走:
1 | Claude Code / 浏览器 |
也就是说:
- 公网访问的是
ai.YOUR_DOMAIN - A 上的 Caddy 负责 HTTPS
- A 上的
127.0.0.1:18317是 FRP 暴露出来的业务端口 - B 上真正提供服务的是
127.0.0.1:8317
四、准备工作
1. 配置域名解析
先把子域名解析到阿里云 ECS 的公网 IP。
例如:
1 | ai.YOUR_DOMAIN -> YOUR_SERVER_IP |
2. 放行安全组端口
A 的安全组至少需要放行:
1 | 80/tcp |
说明:
80/443给 Caddy 使用7000给 frps 使用22给 SSH 使用,建议限制来源 IP
五、A 机配置:Caddy + frps
1. 安装 Caddy
在 A 上执行:
1 | sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg |
2. 安装 frps
1 | FRP_VER=0.68.1 |
3. 配置 frps
编辑配置文件:
1 | sudo nano /etc/frp/frps.toml |
写入:
1 | bindAddr = "0.0.0.0" |
这里有两个关键点:
proxyBindAddr = "127.0.0.1":让业务端口只绑定在 A 本机,不直接对公网开放allowPorts = 18317:只允许映射指定端口,避免随意暴露其他服务
4. 配置 frps systemd 服务
1 | sudo tee /etc/systemd/system/frps.service >/dev/null <<'EOF' |
5. 配置 Caddy
编辑:
1 | sudo nano /etc/caddy/Caddyfile |
写入:
1 | { |
这个配置的作用是:
- 给
ai.YOUR_DOMAIN自动签发 HTTPS 证书 - 把公网请求转发到 A 本机
127.0.0.1:18317 - 屏蔽 CLIProxyAPI 的管理接口,避免直接对公网开放
应用配置:
1 | sudo caddy validate --config /etc/caddy/Caddyfile |
六、B 机配置:CLIProxyAPI + frpc
1. 准备 CLIProxyAPI
如果已经下载好二进制,可以直接使用。
例如:
1 | mkdir -p ~/tools/CLIProxyAPI |
确认可执行文件存在,例如:
1 | /home/YOUR_USERNAME/tools/CLIProxyAPI/cli-proxy-api |
2. 准备配置目录
建议把 CLIProxyAPI 的配置和认证文件统一放在:
1 | ~/.cli-proxy-api |
执行:
1 | mkdir -p ~/.cli-proxy-api |
3. 配置 CLIProxyAPI
编辑:
1 | nano ~/.cli-proxy-api/config.yaml |
写入:
1 | host: "127.0.0.1" |
建议提前生成两串随机密钥:
1 | openssl rand -hex 24 |
分别填到:
api-keysremote-management.secret-key
这里建议特别注意两点:
host: "127.0.0.1"表示 CLIProxyAPI 只监听本机allow-remote: false表示管理接口不允许远程控制
4. 完成 Claude 登录授权
如果 CLIProxyAPI 需要接入 Claude,可以执行:
1 | /home/YOUR_USERNAME/tools/CLIProxyAPI/cli-proxy-api -config /home/YOUR_USERNAME/.cli-proxy-api/config.yaml -claude-login -no-browser |
完成授权后,认证文件会写入:
1 | ~/.cli-proxy-api/ |
5. 配置 CLIProxyAPI 的 systemd 服务
编辑:
1 | sudo nano /etc/systemd/system/cli-proxy-api.service |
写入:
1 | [Unit] |
启动:
1 | sudo systemctl daemon-reload |
先在 B 本机验证服务:
1 | ss -lntp | grep 8317 |
如果能返回模型列表,说明 CLIProxyAPI 在本机已经正常工作。
6. 安装 frpc
1 | FRP_VER=0.68.1 |
7. 配置 frpc
编辑:
1 | sudo nano /etc/frp/frpc.toml |
写入:
1 | serverAddr = "YOUR_SERVER_IP" |
参数说明:
serverAddr:A 的公网 IPserverPort:A 上 frps 的监听端口auth.token:必须和 A 上配置完全一致localPort = 8317:B 上 CLIProxyAPI 的监听端口remotePort = 18317:映射到 A 本机的业务端口
8. 配置 frpc systemd 服务
1 | sudo tee /etc/systemd/system/frpc.service >/dev/null <<'EOF' |
七、验证整条链路
1. 先验证 B 本机服务
1 | curl http://127.0.0.1:8317/v1/models -H "Authorization: Bearer REPLACE_WITH_A_LONG_RANDOM_CLIENT_KEY" |
2. 再验证 A 上的 FRP 映射端口
在 A 上执行:
1 | ss -lntp | grep -E ':7000|:18317' |
如果这一步成功,说明:
1 | A:127.0.0.1:18317 -> B:127.0.0.1:8317 |
已经打通。
3. 最后验证公网 HTTPS 入口
1 | curl https://ai.YOUR_DOMAIN/v1/models -H "Authorization: Bearer REPLACE_WITH_A_LONG_RANDOM_CLIENT_KEY" |
如果能够正常返回模型列表,说明公网访问链路已经全部成立。
八、Claude Code 如何接入
如果后续希望 Claude Code 直接走自己的代理地址,可以在配置里设置:
1 | { |
如果你还做了模型映射,也可以继续追加对应环境变量。
九、常见问题
1. frpc 一直连不上 frps
优先检查:
- A 的安全组是否放行
7000/tcp - A 本机防火墙是否拦截
- B 到 A 的网络是否可达
可以直接测试:
1 | nc -vz YOUR_SERVER_IP 7000 |
2. token in login doesn't match token from configuration
这个报错基本就是 A 和 B 两边的 auth.token 不一致。
检查:
/etc/frp/frps.toml/etc/frp/frpc.toml
确保两边完全相同。
3. curl https://ai.YOUR_DOMAIN 返回 502
通常说明上游链路某一段没通,优先排查:
1 | # B |
4. 不希望管理接口暴露到公网
推荐直接在 Caddy 层拦截:
1 | @denyMgmt path /management* /v0/management* |
同时 CLIProxyAPI 本身也尽量保持:
host: 127.0.0.1allow-remote: false
十、总结
这套方案非常适合下面这种场景:
- 有一台公网服务器
- 有一台高性能但无公网 IP 的本地主机
- 想把本地 AI 服务稳定发布到公网
- 想统一管理 HTTPS、域名和入口安全
最终思路可以概括成一句话:
用阿里云 ECS 做公网入口,用本地高性能主机做实际服务节点,再通过 FRP 和 Caddy 把两者安全串起来。
这样做的好处也很明确:
- 公网入口清晰
- 本地服务不用直接暴露
- HTTPS 统一管理
- 后续扩展其他子域名服务也会比较方便
如果后续继续扩展 rustdesk.YOUR_DOMAIN、cloud.YOUR_DOMAIN 或其他服务,这套架构也可以直接复用。