ENZH
和 AI 讨论这篇文章
ChatGPTClaude

从零搭梯子,这份手册扔给 claude code 直接跑

📊 幻灯片

自建代理服务架构蓝图自建代理服务架构蓝图

这个系列是什么:一份完整的技术 runbook,直接扔给 claude code 就能跑。把手册复制走,占位符(YOUR_VPS_IPyour.domain.com 这些)换成你自己的,发给 agent,让它按步骤执行。背后的故事在实战录同名篇

自建代理,从零到能用

这份手册把自建代理的完整流程走了一遍:VPS 怎么选、协议怎么配、证书怎么申、订阅服务器怎么搭,最后到客户端配置。每一步都是能直接跑的命令,也都是我自己在机器上实际跑过的。你可以自己照着做,也可以整份扔给 agent 让它执行,反正占位符换掉就行。


目录

  1. VPS 选择与初始配置
  2. 第一个坑:shadowsocks 几小时就被封
  3. sing-box 多协议部署
  4. BBR 网络加速
  5. SSL 证书申请与配置
  6. 订阅服务器搭建(nginx)
  7. 节点名称整理
  8. Clash Verge 客户端配置(macOS)
  9. 踩坑记录
  10. 验证清单

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 网络
IPYOUR_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 的流量探针会:

  1. 对可疑端口发送主动探测包
  2. 分析流量的熵值(加密流量熵值很高)
  3. 通过流量特征识别代理协议
  4. 封锁对应 IP 的端口甚至整个 IP

GFW 靠主动探测和熵值分析封杀裸协议,而 VLESS-Reality 伪装成正常 HTTPS 流量绕过识别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 移除掉。如果装 sing-box 之前配了防火墙规则,装完需要验证规则还在不在。默认 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 代理)。原因大概是这样的:

  1. 国内用户无法直接访问 VPS IP 来获取订阅 URL
  2. Cloudflare CDN 在国内可访问,可以中转订阅请求
  3. 代理连接(VLESS、Hysteria2、TUIC 等)使用的是配置文件里硬编码的 VPS IP,不走域名,所以 Cloudflare 代理不影响
  4. 只有订阅的 HTTP 请求走 Cloudflare,实际的代理流量直连 VPS

订阅的 HTTP 请求走 Cloudflare CDN 中转,代理流量则直连配置里硬编码的 VPS IP,两条路径互不干扰订阅的 HTTP 请求走 Cloudflare CDN 中转,代理流量则直连配置里硬编码的 VPS IP,两条路径互不干扰

简单说:申请证书时灰色云朵 → 订阅服务器配好后改橙色云朵。代理协议直连 IP,不受影响。

5.2 获取 Cloudflare API Token

  1. 登录 Cloudflare Dashboard
  2. 我的个人资料API 令牌创建令牌
  3. 用模板:编辑区域 DNS
  4. 区域资源:选你的域名
  5. 创建并保存 Token(只显示一次)

5.3 安装 acme.sh

# 安装(如果 sb.sh 还没装的话)
curl https://get.acme.sh | sh -s email=[email protected]

# 激活环境变量
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 "[email protected]" \
  --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 流量统计

Clash 和 sing-box 客户端能显示上传/下载用量和剩余配额,前提是订阅响应里带 Subscription-Userinfo 这个头。这一节就是把这个头配出来。

安装 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 分支,原版已经停更了。

配置文件位置:

~/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/

8.2 导入订阅

  1. 打开 Clash Verge
  2. 点顶部 订阅
  3. 右上角 +
  4. 输入订阅 URL:https://your.domain.com/YOUR_TOKEN
  5. 导入
  6. 点击刚导入的配置卡片激活它(卡片有高亮边框 = 已激活)

最常见的问题是代理页只显示 DIRECT 和 REJECT、没有节点。那就是配置导入了但没激活,回去重新点一下配置卡片。

8.3 基础设置

设置项推荐值说明
系统代理开启让系统流量走 Clash
模式规则(Rule)按规则分流,国内流量不走代理
TUN 模式可选更彻底的流量接管
混合端口7897HTTP/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 -zvtelnet 测端口显示 Connection refused,但服务其实正常。原因很简单:nc -zvtelnet 默认走 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,延迟高,抗封性还最弱。解法:从 url-test 的 proxies 列表里删掉 Vmess-WS,只留 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 卸载掉。装完重新检查一遍防火墙状态,被移除的话重新安装并配置规则。专用代理 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: cloudflarecf-cache-status: DYNAMIC、200 就成了。代理节点连的是裸 IP,橙云完全不碰它们。


坑 13:CF Token 过期,续期静默失败

现象是证书几个月后过期了,但 acme.sh 的定时续期「看起来」一直在跑。原因是申请证书时用的 Cloudflare API Token 过期或者被吊销了。acme 续期靠这个 Token 改 DNS,Token 一死,续期就失败,而且不会报到你眼前。

解法是申请前先验一次 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 脚本每次安装会随机分配代理协议端口,下表中标注"随机"的端口以实际安装输出为准。

服务端口协议说明
SSHYOUR_SSH_PORTTCP服务器管理
nginx HTTP80TCP自动跳转 HTTPS
nginx HTTPS443TCP订阅服务器
VLESS-Reality(随机)TCP主力节点
Vmess-WS(随机)TCP备用
Hysteria2(随机)UDP高速节点
TUIC-v5(随机)UDP低延迟节点
Anytls(随机)TCPTLS 伪装节点

重要文件

文件用途
/etc/s-box/sb.jsonsing-box 服务端配置
/etc/s-box/clmi.yamlClash 客户端订阅内容
/etc/s-box/sbox.jsonsing-box 客户端订阅内容
/etc/s-box/.sub_token订阅 URL 的访问 Token
/root/ygkkkca/fullchain.crtSSL 证书
/root/ygkkkca/private.keySSL 私钥
/etc/nginx/sites-available/subnginx 站点配置
/etc/s-box/update_traffic.sh流量统计更新脚本
/etc/s-box/.traffic_header.conf生成的 nginx 流量统计头
~/.acme.sh/acme.sh 工作目录
和 AI 讨论这篇文章
ChatGPTClaude
AI 随想第 17 篇 · 共 28 篇

订阅更新

新文章发布时发到你的邮箱,不发别的。


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0