从零搭梯子,这份手册扔给 claude code 直接跑
自建代理服务架构蓝图
这个系列是什么:一份完整的技术 runbook,直接扔给 claude code 就能跑。把手册复制走,占位符(
YOUR_VPS_IP、your.domain.com这些)换成你自己的,发给 agent,让它按步骤执行。背后的故事在实战录同名篇。
自建代理,从零到能用
这份手册把自建代理的完整流程走了一遍:VPS 怎么选、协议怎么配、证书怎么申、订阅服务器怎么搭,最后到客户端配置。每一步都是能直接跑的命令,也都是我自己在机器上实际跑过的。你可以自己照着做,也可以整份扔给 agent 让它执行,反正占位符换掉就行。
目录
- VPS 选择与初始配置
- 第一个坑:shadowsocks 几小时就被封
- sing-box 多协议部署
- BBR 网络加速
- SSL 证书申请与配置
- 订阅服务器搭建(nginx)
- 节点名称整理
- Clash Verge 客户端配置(macOS)
- 踩坑记录
- 验证清单
1. VPS 选择与初始配置
1.1 选 VPS 看什么
选 VPS 我一般看四个东西。这四个直接决定后面封不封、快不快,别的参数都是次要的。
- IP 质量:静态住宅 IP(residential IP)比数据中心 IP 难封得多。Atlas Networks 有西雅图和洛杉矶的住宅 IP,可以考虑。
- 线路质量:从国内连美国,线路首选 9929(AS9929)和 CN2 GIA,延迟低,还能绕开骨干网的拥堵。普通的 AS4837 就差很多。
- 带宽和月流量:至少 100Mbps,月流量 1TB 起步。
- SSH 端口:有些服务商默认给的不是标准端口,记得看开机邮件。
我这台的配置在下面这张表里。后面所有命令都是在这台机器上跑出来的,你照着做的时候,把 IP 和端口换成自己的就行。
| 参数 | 值 |
|---|---|
| 服务商 | Atlas Networks |
| 位置 | 洛杉矶,9929 网络 |
| IP | YOUR_VPS_IP |
| SSH 端口 | YOUR_SSH_PORT |
| 系统 | Ubuntu 24.04.1 LTS |
| 月流量限额 | 3TB |
1.2 初始 SSH 登录
ssh root@YOUR_VPS_IP -p YOUR_SSH_PORT
登录进去先把基础的东西弄好。更新系统、装常用工具、把时区统一设成 UTC,都是一次性的活,一分钟跑完:
# 更新系统
apt-get update && apt-get upgrade -y
# 安装常用工具
apt-get install -y curl wget vim git unzip net-tools
# 设置时区为 UTC(保持一致性)
timedatectl set-timezone UTC
# 确认系统版本
lsb_release -a
1.3 安全加固(推荐)
# 修改 SSH 配置,禁用密码登录(建议先配好公钥再操作)
vim /etc/ssh/sshd_config
# 确保以下两行存在:
# PasswordAuthentication no
# PubkeyAuthentication yes
# 重启 SSH 服务
systemctl restart sshd
# 开启防火墙(ufw),只放行需要的端口
ufw allow YOUR_SSH_PORT/tcp # SSH
ufw allow 80/tcp # HTTP(acme 验证 / nginx)
ufw allow 443/tcp # HTTPS(nginx)
ufw allow 16796/tcp # VLESS-Reality
ufw allow 18877/udp # Hysteria2
ufw allow 31227/udp # TUIC-v5
ufw allow 2082/tcp # Vmess-WS(可选)
ufw enable
ufw status
VPS 服务商的坑:有些服务商默认把
PubkeyAuthentication关掉了。如果公钥已经复制上去,密钥还是登不进,先查这个:grep PubkeyAuthentication /etc/ssh/sshd_config # 如果显示 "no",改掉: sed -i 's/^PubkeyAuthentication no/PubkeyAuthentication yes/' /etc/ssh/sshd_config systemctl restart sshd
2. 第一个坑:shadowsocks 几小时就被封
2.1 当时怎么部署的
最开始我的想法很简单:用 docker 跑一个 shadowsocks-rust,能用就行。当时的命令是这样的:
docker run -d \
--name ss-rust \
--restart always \
-p 8388:8388/tcp \
-p 8388:8388/udp \
-e PASSWORD="your_password" \
ghcr.io/shadowsocks/ssserver-rust:latest \
ssserver -s "0.0.0.0:8388" -m "aes-256-gcm" -k "your_password" --udp-only false
配置参数:
- 端口:8388
- 加密方式:aes-256-gcm
2.2 结果
没过几个小时,IP 就被 GFW 封了。连端口直接超时,从国内完全连不上。
2.3 为什么被封
Shadowsocks(还有没做混淆的 VMess、普通 Socks5)是「裸协议」。GFW 的流量探针会这么干:
- 往可疑端口主动发探测包
- 算流量的熵值(加密流量的熵值很高)
- 看流量特征,认出是哪种代理协议
- 封掉这个 IP 的端口,甚至整个 IP
GFW 靠主动探测和熵值分析封杀裸协议,而 VLESS-Reality 伪装成正常 HTTPS 流量绕过识别
反正结论就一条,给国内用的 IP 不能裸跑 SS/VMess/Socks5,必须换成能混淆流量的新协议。所以下一节换成了 sing-box。
3. sing-box 多协议部署
3.1 为什么选 sing-box
sing-box 是目前最成熟的多协议代理内核,支持:
- VLESS + Reality:借真实网站的 TLS 指纹,GFW 分不出这是代理还是正常的 HTTPS,很难被封
- Hysteria2:跑在 QUIC/UDP 上,速度快,适合带宽大的场景
- TUIC v5:也跑在 UDP 上,延迟低
3.2 一键脚本安装
# 一键安装
bash <(wget -qO- https://raw.githubusercontent.com/yonggekkk/sing-box-yg/main/sb.sh)
脚本第一次跑完会存到 /root/sb.sh,之后直接敲 sb 这个快捷命令就能进管理菜单。
3.3 安装过程
脚本跑起来会出一个菜单。先选 1(安装),然后按 Enter 用默认配置就行。脚本会自己做完这几件事:
- 安装 sing-box 服务
- 生成 5 个协议的配置
- 写入 systemd 服务
- 启动服务
注意:sing-box 的安装脚本装着装着,可能会把
ufw删掉。如果你装之前配了防火墙规则,装完得看看规则还在不在。iptables 默认策略是 ACCEPT(等于没有防火墙),只跑代理的 VPS 这样也能接受,但别忘了查一下。
3.4 5 个协议一览
| 协议 | 默认端口 | 传输层 | 抗封性 | 备注 |
|---|---|---|---|---|
| VLESS-Reality-Vision | (随机) | TCP | ★★★★★ | 首选,伪装成 HTTPS 流量 |
| Vmess-WebSocket | (随机) | TCP | ★★ | 最弱,不建议日常用 |
| Hysteria2 | (随机) | UDP | ★★★★ | 速度最快 |
| TUIC-v5 | (随机) | UDP | ★★★★ | 低延迟 |
| Anytls | (随机) | TCP | ★★★★ | TLS 伪装,较新协议 |
注意:脚本每次装都会随机分端口,别照搬旧教程里的端口号。装完看输出,确认实际用的是哪个端口。
3.5 查看配置文件
# 所有配置文件在此
ls /etc/s-box/
# Clash Mihomo 客户端配置(包含所有节点)
cat /etc/s-box/clmi.yaml
# sing-box 客户端配置
cat /etc/s-box/sbox.json
3.6 验证服务状态
# 查看 sing-box 服务状态
systemctl status sing-box
# 查看实时日志
journalctl -u sing-box -f
# 确认 TCP 端口监听
ss -tlnp | grep -E "16796|2082"
# Hysteria2 和 TUIC 是 UDP,用 -u
ss -ulnp | grep -E "18877|31227"
3.7 常用管理命令
# 启动/停止/重启
systemctl start sing-box
systemctl stop sing-box
systemctl restart sing-box
# 查看服务端配置
cat /etc/s-box/sb.json
# 进入管理菜单
sb
4. BBR 网络加速
BBR 是 Google 做的 TCP 拥塞控制算法,开了以后跨国连接能快不少。Linux 4.9+ 的内核已经自带,打开就行。
4.1 通过脚本启用
# 进入管理菜单
sb
# 选择选项 11(BBR 加速)
4.2 手动启用
# 检查内核版本(必须 >= 4.9)
uname -r
# 编辑 sysctl 配置
cat >> /etc/sysctl.conf << 'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
# 应用
sysctl -p
# 验证
sysctl net.ipv4.tcp_congestion_control
# 应该输出:net.ipv4.tcp_congestion_control = bbr
# 确认 BBR 模块已加载
lsmod | grep bbr
5. SSL 证书申请与配置
Reality 协议本身用不着 SSL 证书,但 Hysteria2 和 TUIC 要,nginx 订阅服务器也得走 HTTPS,所以证书这一步逃不掉。
5.1 DNS 配置
在 Cloudflare 给域名加一条 A 记录:
类型: A
名称: sub(或你想要的子域名)
IPv4 地址: YOUR_VPS_IP
代理状态: 仅 DNS(灰色云朵)— 初始证书申请用
灰云和橙云得在对的时候切。第一次申请 SSL 证书时用灰色云朵(仅 DNS),等订阅服务器配好了(第 6 节),再改成橙色云朵(开 Cloudflare 代理)。原因大概是这样:
- 国内用户没法直接连 VPS IP 去拿订阅 URL
- Cloudflare CDN 在国内连得上,可以帮忙中转订阅请求
- 代理连接(VLESS、Hysteria2、TUIC 等)用的是配置文件里硬编码的 VPS IP,不走域名,所以开不开 Cloudflare 代理都没关系
- 只有订阅的 HTTP 请求走 Cloudflare,实际的代理流量直连 VPS
订阅的 HTTP 请求走 Cloudflare CDN 中转,代理流量则直连配置里硬编码的 VPS IP,两条路径互不干扰
简单说:申请证书时用灰色云朵 → 订阅服务器配好后改成橙色云朵。代理协议直连 IP,不受影响。
5.2 获取 Cloudflare API Token
- 登录 Cloudflare Dashboard
我的个人资料→API 令牌→创建令牌- 用模板:
编辑区域 DNS - 区域资源:选你的域名
- 创建并保存 Token(只显示一次)
5.3 安装 acme.sh
# 安装(如果 sb.sh 还没装的话)
curl https://get.acme.sh | sh -s email=your@email.com
# 激活环境变量
source ~/.bashrc
# 或直接用完整路径
~/.acme.sh/acme.sh --version
5.4 清理 acme.sh 缓存(重要!)
这里有个坑,卡了我不少时间。sb.sh 跑的时候会调 acme.sh,往账户缓存里写进一个错的邮件地址。不清掉的话,后面申请证书会一直报错:
# 清理错误的账户缓存
rm -rf ~/.acme.sh/ca/ ~/.acme.sh/*.json
# 确认清理完毕
ls ~/.acme.sh/
5.5 申请证书
# 设置 Cloudflare API Token
export CF_Token="你的Cloudflare_API_Token"
# 申请 ECC 证书(比 RSA 更小更快)
~/.acme.sh/acme.sh --issue \
--dns dns_cf \
-d your.domain.com \
--ecc \
--server letsencrypt \
--accountemail "your@gmail.com" \
--force
# 完成后显示证书路径,类似:
# /root/.acme.sh/your.domain.com_ecc/your.domain.com.cer
# /root/.acme.sh/your.domain.com_ecc/your.domain.com.key
# /root/.acme.sh/your.domain.com_ecc/fullchain.cer
参数说明:
--dns dns_cf:用 Cloudflare DNS API 验证,不用开 80 端口--ecc:椭圆曲线证书--server letsencrypt:Let's Encrypt CA--force:强制重新申请
5.6 安装证书到指定路径
# 创建目标目录
mkdir -p /root/ygkkkca
# 安装证书,设置自动重载
~/.acme.sh/acme.sh --install-cert \
-d your.domain.com \
--ecc \
--cert-file /root/ygkkkca/cert.crt \
--key-file /root/ygkkkca/private.key \
--fullchain-file /root/ygkkkca/fullchain.crt \
--reloadcmd "systemctl restart sing-box"
这一步顺便把自动续期也设好了。证书到期前 30 天,acme.sh 会自己去申请新证书,再跑 --reloadcmd 把服务重启。
5.7 复制证书到 sing-box 目录
# 复制到 sing-box 读取的路径
cp /root/ygkkkca/fullchain.crt /etc/s-box/cert.pem
cp /root/ygkkkca/private.key /etc/s-box/private.key
# 设置权限
chmod 600 /etc/s-box/private.key
# 重启 sing-box
systemctl restart sing-box
# 验证
systemctl status sing-box
5.8 验证证书
# 查看证书信息
openssl x509 -in /root/ygkkkca/fullchain.crt -noout -text | grep -E "Subject:|Not After"
# 查看 acme.sh 管理的证书列表
~/.acme.sh/acme.sh --list
6. 订阅服务器搭建(nginx)
搭订阅服务器就是图省事。用 nginx 给出一个订阅 URL,Clash/sing-box 客户端点一下就能更新,不用每次手动改节点配置。
先确认 80/443 没被运营商封:有些机房默认在网络边缘把进来的 80/443 丢掉,只放行高位端口。这时候 nginx 在本地
listen 443一切正常,从外网就是连不上。所以配好以后一定要从外面测一次(详见坑 12)。如果 443 被封了,就把订阅服务器挪到一个放行的高位端口,订阅 URL 带上端口就行。代理协议走的是自己的高位端口,不受影响。
6.1 安装 nginx
apt-get install -y nginx
# 启动并设置开机自启
systemctl enable nginx
systemctl start nginx
systemctl status nginx
6.2 生成随机订阅 Token
Token 就是订阅 URL 的路径,相当于密码。别人不知道这个路径,就拿不到你的配置:
# 生成 16 位随机 token
TOKEN=$(cat /proc/sys/kernel/random/uuid | tr -d "-" | head -c 16)
echo "你的 Token 是: $TOKEN"
# 保存 token
echo $TOKEN > /etc/s-box/.sub_token
cat /etc/s-box/.sub_token
6.3 修正 Clash 配置文件
用 nginx 发配置之前,先把证书验证的设置改对。打开 Clash Mihomo 的客户端配置:
# 编辑 Clash Mihomo 客户端配置
vim /etc/s-box/clmi.yaml
找到这几行改掉:
# 修改前:
skip-cert-verify: true
# 修改后:
skip-cert-verify: false
# 同时找到 sni 字段,改为你的真实域名:
sni: your.domain.com
用 sed 批量替换更省事:
# 关闭跳过证书验证
sed -i 's/skip-cert-verify: true/skip-cert-verify: false/g' /etc/s-box/clmi.yaml
# 检查
grep "skip-cert-verify" /etc/s-box/clmi.yaml
6.4 配置 nginx
TOKEN=$(cat /etc/s-box/.sub_token)
cat > /etc/nginx/sites-available/sub << EOF
server {
listen 80;
listen 443 ssl;
server_name your.domain.com;
ssl_certificate /root/ygkkkca/fullchain.crt;
ssl_certificate_key /root/ygkkkca/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /${TOKEN} {
alias /etc/s-box/clmi.yaml;
default_type "text/plain; charset=utf-8";
}
location /singbox/${TOKEN} {
alias /etc/s-box/sbox.json;
default_type "application/json; charset=utf-8";
}
location / {
return 404;
}
}
EOF
6.5 启用站点
# 创建软链接
ln -sf /etc/nginx/sites-available/sub /etc/nginx/sites-enabled/sub
# 删除默认站点(避免冲突)
rm -f /etc/nginx/sites-enabled/default
# 测试配置
nginx -t
# 重载
systemctl reload nginx
6.6 流量统计
只要订阅响应里带上 Subscription-Userinfo 这个头,Clash 和 sing-box 客户端就能显示上传/下载用了多少、还剩多少额度。这一节就是把这个头配出来。
安装 vnstat
apt-get install -y vnstat
systemctl enable vnstat
systemctl start vnstat
# 查看网卡名称——别照抄教程里的 ens3
ip -o -4 route show to default | awk '{print $5}'
# 较新的 Ubuntu/KVM 镜像多是 ens17,老的才是 ens3/eth0。以实际输出为准
创建流量统计脚本
cat > /etc/s-box/update_traffic.sh << 'SCRIPT'
#!/bin/bash
IFACE="ens17" # 替换为你的网卡名(先用上面的 ip route 命令确认,别想当然填 ens3)
MONTHLY_LIMIT_GB=3000 # 月流量限额(GB),3000 = 3TB
MONTHLY_LIMIT_BYTES=$((MONTHLY_LIMIT_GB * 1073741824))
STATS=$(vnstat -i "$IFACE" --json m 2>/dev/null)
if [ -z "$STATS" ]; then
UPLOAD=0
DOWNLOAD=0
else
UPLOAD=$(echo "$STATS" | python3 -c "
import sys, json
data = json.load(sys.stdin)
months = data.get('interfaces', [{}])[0].get('traffic', {}).get('month', [])
print(months[-1].get('tx', 0) if months else 0)
" 2>/dev/null || echo 0)
DOWNLOAD=$(echo "$STATS" | python3 -c "
import sys, json
data = json.load(sys.stdin)
months = data.get('interfaces', [{}])[0].get('traffic', {}).get('month', [])
print(months[-1].get('rx', 0) if months else 0)
" 2>/dev/null || echo 0)
fi
EXPIRE=$(date -d "$(date +%Y-%m-01) +1 month" +%s 2>/dev/null || date -d "next month" +%s)
cat > /etc/s-box/.traffic_header.conf << EOF
add_header Subscription-Userinfo "upload=${UPLOAD}; download=${DOWNLOAD}; total=${MONTHLY_LIMIT_BYTES}; expire=${EXPIRE}";
EOF
SCRIPT
chmod +x /etc/s-box/update_traffic.sh
bash /etc/s-box/update_traffic.sh
设置定时任务
# 每 5 分钟更新一次
(crontab -l 2>/dev/null; echo "*/5 * * * * /bin/bash /etc/s-box/update_traffic.sh > /dev/null 2>&1") | crontab -
更新 nginx 配置
在每个订阅 location 块里加 include /etc/s-box/.traffic_header.conf;:
TOKEN=$(cat /etc/s-box/.sub_token)
cat > /etc/nginx/sites-available/sub << EOF
server {
listen 80;
listen 443 ssl;
server_name your.domain.com;
ssl_certificate /root/ygkkkca/fullchain.crt;
ssl_certificate_key /root/ygkkkca/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /${TOKEN} {
alias /etc/s-box/clmi.yaml;
default_type "text/plain; charset=utf-8";
add_header Content-Disposition "inline";
include /etc/s-box/.traffic_header.conf;
}
location /singbox/${TOKEN} {
alias /etc/s-box/sbox.json;
default_type "application/json; charset=utf-8";
include /etc/s-box/.traffic_header.conf;
}
location / {
return 404;
}
}
EOF
nginx -t && systemctl reload nginx
客户端刷新一下订阅,就能看到流量用量的进度条了。
6.7 验证订阅 URL
TOKEN=$(cat /etc/s-box/.sub_token)
echo "Clash 订阅 URL: https://your.domain.com/${TOKEN}"
echo "Sing-box 订阅 URL: https://your.domain.com/singbox/${TOKEN}"
# 本地测试
curl -s "https://your.domain.com/${TOKEN}" | head -20
7. 节点名称整理
7.1 问题
sb.sh 生成的节点名后面带一串机器 ID,在客户端里看着又丑,又看不出是哪个协议:
vless-reality-vision-C20260225127975
hysteria2-C20260225127975
7.2 重命名
# 备份
cp /etc/s-box/clmi.yaml /etc/s-box/clmi.yaml.bak
# 用 sed 替换
sed -i "s/vless-reality-vision-C[0-9A-Za-z]*/⭐ VLESS-Reality 首选/g" /etc/s-box/clmi.yaml
sed -i "s/hysteria2-C[0-9A-Za-z]*/🚀 Hysteria2 快速/g" /etc/s-box/clmi.yaml
sed -i "s/tuic-C[0-9A-Za-z]*/⚡ TUIC-v5 低延迟/g" /etc/s-box/clmi.yaml
sed -i "s/vmess-ws-C[0-9A-Za-z]*/🔸 Vmess-WS 备用/g" /etc/s-box/clmi.yaml
sed -i "s/anytls-C[0-9A-Za-z]*/🔒 Anytls 隐匿/g" /etc/s-box/clmi.yaml
7.3 从自动选择组里移除 Vmess-WS
Vmess-WS 是五个协议里最弱的,容易被 GFW 认出来,不该放进自动选择组。先看一下现在的配置:
# 查看当前配置
grep -A 20 "proxy-groups" /etc/s-box/clmi.yaml | head -30
找到自动选择组:
proxy-groups:
- name: 自动选择
type: url-test
proxies:
- ⭐ VLESS-Reality 首选
- 🚀 Hysteria2 快速
- ⚡ TUIC-v5 低延迟
- 🔸 Vmess-WS 备用 # 删掉这一行
url: http://www.gstatic.com/generate_204
interval: 300
手动编辑,把 Vmess-WS 那一行删掉:
vim /etc/s-box/clmi.yaml
改完验一下 YAML 格式没坏:
python3 -c "import yaml; yaml.safe_load(open('/etc/s-box/clmi.yaml'))" && echo "YAML 格式正确"
7.4 让修改生效
如果改的是 nginx 发出去的那份配置文件,客户端下次更新订阅就会拿到新的,不用重启 sing-box。只有改了 sing-box 自己的配置(比如端口、证书路径)才要重启:
systemctl restart sing-box
systemctl status sing-box
8. Clash Verge 客户端配置(macOS)
8.1 安装
推荐用 Clash Verge Rev 这个分支,原版已经停更了。
- 下载:https://github.com/clash-verge-rev/clash-verge-rev/releases
- macOS 下载
.dmg - Bundle ID:
io.github.clash-verge-rev.clash-verge-rev
配置文件位置:
~/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/
8.2 导入订阅
- 打开 Clash Verge
- 点顶部
订阅 - 右上角
+ - 输入订阅 URL:
https://your.domain.com/YOUR_TOKEN - 点
导入 - 点击刚导入的配置卡片激活它(卡片有高亮边框 = 已激活)
最常见的问题是代理页只有 DIRECT 和 REJECT,看不到节点。这是配置导入了但没激活,回去再点一下配置卡片就行。
8.3 基础设置
| 设置项 | 推荐值 | 说明 |
|---|---|---|
| 系统代理 | 开启 | 让系统流量走 Clash |
| 模式 | 规则(Rule) | 按规则分流,国内流量不走代理 |
| TUN 模式 | 可选 | 更彻底的流量接管 |
| 混合端口 | 7897 | HTTP/SOCKS5 混合端口(默认) |
8.4 代理组配置
代理页我推荐这么设:
- GLOBAL:选
自动选择 - 自动选择(url-test):放 VLESS-Reality、Hysteria2、TUIC-v5,自动测速,挑延迟最低的
- 手动切换:想用哪个节点时自己选
8.5 连接测试
# macOS 终端
curl -x socks5://127.0.0.1:7897 https://www.google.com -I
# 或 HTTP 代理
curl -x http://127.0.0.1:7897 https://ip.me
再用浏览器打开 https://ip.me,看显示的是不是 VPS 的 IP。
8.6 更新订阅
服务端配置改了以后,在订阅页点一下配置卡片右边的刷新按钮就行。
9. 踩坑记录
下面这 13 个坑全是我自己踩过的,按现象、原因、解法记下来。遇到同样现象的话直接对着查,能省不少时间。
坑 1:SS/裸 VMess 被秒封
SS 部署完没几个小时,IP 就被封了。原因前面讲过,GFW 会主动去探可疑的端口,而 SS 的流量特征太明显。换成 VLESS-Reality 或 Hysteria2 就行。反正给国内用的 VPS,裸跑 SS/VMess 基本撑不过一天。
坑 2:acme.sh 账户缓存错误
acme.sh 申请证书报错,说邮件对不上。这是因为 sb.sh 调 acme.sh 的时候,往账户缓存里写了个错的邮件地址,把缓存清掉就好:
rm -rf ~/.acme.sh/ca/ ~/.acme.sh/*.json
清完重新申请,记得加上 --accountemail 和 --force。
坑 3:Cloudflare 橙色云朵的正确时机
申请证书时开着橙色代理,acme.sh 就验证不过。因为橙色代理会把真实 IP 藏起来,acme.sh 验证证书时就被干扰了。所以申请证书时用灰色云朵(仅 DNS),等订阅服务器配好了再改成橙色云朵,这样国内用户也能通过 Cloudflare CDN 拿到订阅。代理协议用的是配置文件里硬编码的 IP,不受影响。
坑 4:Hysteria2 和 TUIC 是 UDP
用 nc -zv 或 telnet 测端口,显示 Connection refused,可服务其实好好的。原因很简单,nc -zv 和 telnet 默认走 TCP,而 Hysteria2 和 TUIC 走的是 UDP。换成 UDP 模式测就行:
# UDP 模式测试
nc -zuv YOUR_IP 18877
# 或在服务端查看
ss -ulnp | grep -E "18877|31227"
坑 5:Clash Verge 代理页只显示 DIRECT/REJECT
导入订阅之后看不到节点,多半不是订阅坏了,是配置导入了但没激活。回到订阅页点一下配置卡片,出现高亮边框就行。
坑 6:Vmess-WS 不该放在自动选择组
自动选择偶尔会切到 Vmess-WS,它延迟高,还最容易被封。把 Vmess-WS 从 url-test 的 proxies 列表里删掉,只留 VLESS-Reality、Hysteria2、TUIC-v5 就行。
坑 7:证书路径需要手动同步
acme.sh 续期成功了,sing-box 却还在用旧证书。因为两边路径不一样,acme.sh 把证书装在 /root/ygkkkca/,sing-box 读的是 /etc/s-box/cert.pem,这两处不会自己同步。直接把复制这一步加进 reloadcmd:
~/.acme.sh/acme.sh --install-cert \
-d your.domain.com \
--ecc \
--cert-file /root/ygkkkca/cert.crt \
--key-file /root/ygkkkca/private.key \
--fullchain-file /root/ygkkkca/fullchain.crt \
--reloadcmd "cp /root/ygkkkca/fullchain.crt /etc/s-box/cert.pem && cp /root/ygkkkca/private.key /etc/s-box/private.key && systemctl restart sing-box"
坑 8:Clash Verge 重启后配置丢失
重启 Clash Verge 以后,手动改的设置没了。在客户端里直接改配置就会这样,一重启就可能被覆盖,这个没什么好修的。所以节点重命名和各种优化,全都放到服务端的 YAML 文件里做,再通过订阅 URL 发下去。客户端只管拉下来用,别在本地改。
坑 9:PubkeyAuthentication 被服务商关闭
公钥已经复制到 VPS 上了,密钥还是登不进去,照样让你输密码。这是因为有些 VPS 服务商默认在 sshd_config 里把 PubkeyAuthentication 关了。这样改:
grep PubkeyAuthentication /etc/ssh/sshd_config
# 如果显示 "no":
sed -i 's/^PubkeyAuthentication no/PubkeyAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd
坑 10:sing-box 脚本会删除 ufw
装 sing-box 之前配好了 ufw,装完一跑 ufw status,直接报命令不存在,因为 sb.sh 装的时候可能把 ufw 卸掉了。所以装完要再查一遍防火墙,ufw 没了就重新装上,把规则再配一遍。如果这台 VPS 只跑代理,iptables 默认的 ACCEPT 策略也能接受。
坑 11:国内无法获取订阅 URL
国内用户手上要是没有现成的 VPN,直接连 VPS IP 是拿不到订阅文件的,因为 IP 可能被墙了,或者国内网络直接连不过去。给订阅域名开上 Cloudflare 橙色代理(CDN)就行。Cloudflare CDN 在国内连得上,可以帮忙中转订阅请求;代理配置文件里用的是直连 IP,不受影响。
坑 12:运营商在边缘封了 80/443
sing-box 的代理端口都通,订阅 URL 却怎么都打不开,在服务器上用 ss -tlnp 一看,nginx 明明在监听 80、443。原因是有些机房(实测台湾原生 IP 这类)默认在网络边缘把进来的 80/443 丢掉,只放行高位端口。本地监听一切正常,外网就是连不进来。
所以要从外面测,别只看服务器内部:
# 从你自己的机器上测,不是在服务器上(nc -G 是 macOS/BSD 的;Linux 用 -w)
for p in 443 2096 YOUR_TCP_PORT; do nc -z -G6 YOUR_VPS_IP $p && echo "$p 通" || echo "$p 被封"; done
# 注:只测 TCP。Hysteria2/TUIC 是 UDP,得用坑 4 的 nc -zuv 测
两种修法:
快的修法,是让 nginx 再多监听一个放行的高位端口(比如 2096),订阅 URL 带上端口 https://your.domain.com:2096/TOKEN。改完用 systemctl restart nginx(动了 listen 指令,restart 最稳)。代理协议走自己的高位端口,不受影响。
干净的修法我更推荐,还能顺手解决国内拉订阅的问题。拿高位端口当源站,订阅子域名开 Cloudflare 橙云,再加一条 Origin Rule,把回源端口改成 2096。这样客户端连的是 CF 的标准 443,URL 不用带端口,CF 自己用 2096 回源。你的 API Token 得有 Zone > Origin Rules > Edit 权限,zone 的 SSL 模式得是 Full 档(如果已经有别的橙云记录在跑,那就已经是了)。
# 1) 订阅记录切橙云
curl -s -X PATCH ".../zones/$ZID/dns_records/$RID" -H "Authorization: Bearer $CF" --data '{"proxied":true}'
# 2) Origin Rule:CF 回源走 2096
curl -s -X PUT ".../zones/$ZID/rulesets/phases/http_request_origin/entrypoint" \
-H "Authorization: Bearer $CF" \
--data '{"rules":[{"action":"route","action_parameters":{"origin":{"port":2096}},"expression":"(http.host eq \"sub.your.domain.com\")","enabled":true}]}'
最后跑 curl -sI https://your.domain.com/TOKEN 验一下,看到 server: cloudflare、cf-cache-status: DYNAMIC 和 200 就成了。代理节点连的是裸 IP,橙云完全碰不到它们。
坑 13:CF Token 过期,续期静默失败
证书几个月后过期了,可 acme.sh 的定时续期「看起来」一直在跑。原因是申请证书时用的 Cloudflare API Token 过期了,或者被吊销了。acme 续期要靠这个 Token 去改 DNS,Token 一死,续期就失败,而且不会报到你眼前。
所以申请前先验一次 Token,平时用不过期的 Token,别用临时的。验的时候别用 /user/tokens/verify,那是 user 级的接口,碰上「只授权了某个 zone」的 Token,它会误报 Invalid API Token,明明能用也说无效。真去读一次 zone 来验才准:
# 真实功能验证,不是 /user/tokens/verify
curl -s "https://api.cloudflare.com/client/v4/zones?name=your-zone.com" \
-H "Authorization: Bearer $CF_Token" | python3 -m json.tool
# success: true 且 result 非空才往下走
(真死掉的 Token 两个接口都过不了;zone 范围的好 Token 只是过不了 /user/tokens/verify 而已。)
加第二个节点别从头来:已经有一个能用的节点,再开一台时直接克隆最省事。把
/etc/s-box整个打包搬过去,sb.json和 sing-box 二进制跟机器没关系(监听::、Reality 借公共 SNI、证书走路径),照搬就行。只要改三个地方,客户端配置里的 IP(用sed批量换掉旧 IP),节点名里的机器 ID,还有给新域名单独申一张证书。这比重跑一遍交互脚本快得多,也不会手抖配错。
10. 验证清单
按顺序一项一项确认,全都通过,服务就没问题。服务端七项,客户端三项,最后再看一眼 Clash Verge 界面上的状态。
服务端
# 1. sing-box 运行正常
systemctl is-active sing-box
# 期望:active
# 2. 所有端口监听
ss -tlnp | grep -E "16796|2082" # TCP
ss -ulnp | grep -E "18877|31227" # UDP
# 3. BBR 已启用
sysctl net.ipv4.tcp_congestion_control
# 期望:bbr
# 4. SSL 证书有效
openssl x509 -in /root/ygkkkca/fullchain.crt -noout -dates
# 确认 notAfter 在未来
# 5. nginx 运行正常
systemctl is-active nginx
# 期望:active
# 6. 订阅 URL 可访问
TOKEN=$(cat /etc/s-box/.sub_token)
curl -sk "https://your.domain.com/${TOKEN}" | head -5
# 期望看到 YAML 配置内容
# 7. 证书与私钥匹配
openssl x509 -in /etc/s-box/cert.pem -pubkey -noout | openssl sha256
openssl pkey -in /etc/s-box/private.key -pubout | openssl sha256
客户端
# 确认系统代理
networksetup -getwebproxy Wi-Fi
# 期望:Enabled: Yes, Server: 127.0.0.1, Port: 7897
# 测试代理
curl -x http://127.0.0.1:7897 https://ip.me
# 期望显示 VPS IP
# 测试 Google
curl -x http://127.0.0.1:7897 -I https://www.google.com
# 期望:HTTP/2 200
Clash Verge 界面
| 检查项 | 期望状态 |
|---|---|
| 订阅页 → 配置卡片 | 高亮边框(已激活) |
| 代理页 → 节点列表 | 显示 VLESS-Reality、Hysteria2、TUIC-v5 |
| 设置页 → 系统代理 | 已开启 |
| 设置页 → 代理模式 | 规则(Rule) |
| 代理页 → 延迟测试 | 各节点显示延迟数值 |
自动续期
# 查看定时任务
crontab -l | grep acme
# 干跑续期(不实际更新)
~/.acme.sh/acme.sh --renew -d your.domain.com --ecc --dry-run
附录:快速参考
常用命令
# sing-box 管理
sb # 管理菜单
systemctl restart sing-box # 重启
journalctl -u sing-box -f # 实时日志
# 证书管理
~/.acme.sh/acme.sh --list # 列出所有证书
~/.acme.sh/acme.sh --renew -d your.domain.com --ecc # 手动续期
# 订阅
TOKEN=$(cat /etc/s-box/.sub_token)
echo "Clash 订阅: https://your.domain.com/${TOKEN}"
# nginx
nginx -t # 测试配置
systemctl reload nginx # 热重载
端口总览
注意:sing-box 脚本每次装都会随机分配代理协议的端口,下表里标了"随机"的端口,以你实际安装时的输出为准。
| 服务 | 端口 | 协议 | 说明 |
|---|---|---|---|
| SSH | YOUR_SSH_PORT | TCP | 服务器管理 |
| nginx HTTP | 80 | TCP | 自动跳转 HTTPS |
| nginx HTTPS | 443 | TCP | 订阅服务器 |
| VLESS-Reality | (随机) | TCP | 主力节点 |
| Vmess-WS | (随机) | TCP | 备用 |
| Hysteria2 | (随机) | UDP | 高速节点 |
| TUIC-v5 | (随机) | UDP | 低延迟节点 |
| Anytls | (随机) | TCP | TLS 伪装节点 |
重要文件
| 文件 | 用途 |
|---|---|
/etc/s-box/sb.json | sing-box 服务端配置 |
/etc/s-box/clmi.yaml | Clash 客户端订阅内容 |
/etc/s-box/sbox.json | sing-box 客户端订阅内容 |
/etc/s-box/.sub_token | 订阅 URL 的访问 Token |
/root/ygkkkca/fullchain.crt | SSL 证书 |
/root/ygkkkca/private.key | SSL 私钥 |
/etc/nginx/sites-available/sub | nginx 站点配置 |
/etc/s-box/update_traffic.sh | 流量统计更新脚本 |
/etc/s-box/.traffic_header.conf | 生成的 nginx 流量统计头 |
~/.acme.sh/ | acme.sh 工作目录 |

