本文演示在已有 Caddy、3x-ui 等服务占用常见 TCP 端口的 VPS 上,部署 Hysteria2,使用 Cloudflare DNS-01 自动申请和续签 Let’s Encrypt 证书,并配置 UDP 端口跳跃与 Windows v2rayN 和ios小火箭客户端。
写在前面
Hysteria2 基于 QUIC/UDP,适合合法自用、远程办公、授权测试和个人网络环境优化。请遵守所在地法律、VPS 服务条款和网络服务规定。
本文采用下面这套结构:
h2.example.com(Cloudflare 灰云 DNS)
│
▼
VPS UDP 20000-21000
│
iptables 自动重定向
│
▼
Hysteria2 实际监听 UDP 20000
这种方案有三个优点:
- 不占用 Caddy、Nginx 或 3x-ui 使用的 TCP 443/8443;
- 使用 Cloudflare DNS-01 验证,不需要抢占 TCP 80/443;
- Let’s Encrypt 证书受 Windows、sing-box 和主流客户端信任,并能自动续签。
需要特别说明:Cloudflare 普通橙云 CDN 不能代理 Hysteria2。Hysteria2 虽然在握手阶段表现得像 HTTP/3,但认证后使用自己的协议,因此 DNS 记录必须保持“仅 DNS/灰云”。
一、准备工作
开始前准备:
- 一台有公网 IPv4 或 IPv6 的 VPS;
- Debian 12/13、Ubuntu 22.04/24.04 等主流 Linux 系统;
- root 或 sudo 权限;
- 一个由 Cloudflare 管理 DNS 的域名;
- Windows 电脑和较新的 v2rayN;
- VPS 服务商后台安全组的管理权限。
本文示例参数:
域名:h2.example.com 端口范围:UDP 20000-21000 认证方式:password 混淆方式:salamander 客户端:v2rayN + sing-box
请将 h2.example.com、邮箱和端口范围替换成自己的值。
二、配置 Cloudflare DNS
进入 Cloudflare:
网站 → 选择域名 → DNS → 记录
添加 A 记录:
| 类型 | 名称 | 内容 | TTL | 代理状态 |
|---|---|---|---|---|
| A | h2 | VPS 公网 IPv4 | Auto | 仅 DNS(灰云) |
如果使用 IPv6,再添加 AAAA 记录。不要添加错误的 AAAA 记录,否则客户端可能优先连接到错误地址。
本地检查:
nslookup h2.example.com
VPS 检查公网 IPv4:
curl -4s https://api.ipify.org echo
两边 IP 应一致。
三、创建 Cloudflare DNS API Token
进入 Cloudflare 个人资料中的 API Token 页面,新建自定义 Token。
权限建议:
Zone / DNS / Edit Zone / Zone / Read
区域资源限制为:
Include / Specific zone / example.com
客户端 IP 地址过滤可以留空,也可以限制为 VPS 的固定公网 IP。完成摘要页后,必须继续点击“创建令牌”,并使用下一页只显示一次的完整 Token。不要误用 Global API Key、Zone ID 或 Origin CA Key。
在 VPS 上验证 Token。为了避免下划线转义问题,本文使用变量名 CFTOKEN。下面的输入命令不会把 Token 写进 Shell 历史;粘贴时字符不显示属于正常现象:
read -rsp '粘贴完整的 Cloudflare API Token,然后按回车:' CFTOKEN
echo
printf 'Token 长度=%s\n' "${#CFTOKEN}"
CFURL='https:'//api.cloudflare.com/client/v4/user/tokens/verify
curl -sS "$CFURL" \
-H "Authorization: Bearer ${CFTOKEN}" \
-H "Content-Type: application/json"
echo
正确结果应包含:
"status":"active" "success":true
Token 后续用于自动续签证书,因此部署完成后不要删除或撤销。Token 必须限制到指定域名和最小权限。
四、检查端口占用
Hysteria2 使用 UDP。即使某程序占用了 TCP 8443,只要 UDP 8443 空闲,两者仍可共用同一个端口号。
查看 TCP/UDP 443:
ss -lntp 'sport = :443' ss -lnup 'sport = :443'
查看 UDP 20000:
ss -lnup 'sport = :20000'
如果 Caddy 同时占用 TCP 和 UDP 443,通常是 Caddy 启用了 HTTP/3。本文直接选择独立的 UDP 端口范围 20000-21000,不修改现有网站配置。
五、安装 Hysteria2
安装基础工具:
apt-get update apt-get install -y curl openssl ca-certificates iptables
执行 Hysteria2 官方安装脚本:
bash <(curl -fsSL https://get.hy2.sh/)
检查版本:
hysteria version
官方脚本会安装:
/usr/local/bin/hysteria /etc/hysteria/config.yaml /etc/systemd/system/hysteria-server.service
六、生成认证密码和混淆密码
为了便于复制到客户端,本文使用十六进制密码,避免 Base64 中的 +、/、= 在分享链接里需要额外转义:
AUTH="$(openssl rand -hex 24)" OBFS="$(openssl rand -hex 24)"
确认变量非空:
printf 'AUTH 长度=%s,OBFS 长度=%s\n' "${#AUTH}" "${#OBFS}"
两者都应为 48。保存到仅 root 可读的文件:
umask 077 printf 'AUTH=%s\nOBFS=%s\n' "$AUTH" "$OBFS" \ > /root/hysteria2-credentials.txt
七、先用单端口申请证书
建议先让 Hysteria2 监听单端口 20000,确认 Cloudflare Token 和证书申请正常后,再开启端口跳跃。这样即使 ACME 配置错误,也不会留下未清理的防火墙跳跃链。
写入配置:
cat > /etc/hysteria/config.yaml <<EOF
listen: ":20000"
acme:
domains:
- h2.example.com
email: [email protected]
ca: letsencrypt
type: dns
dns:
name: cloudflare
config:
cloudflare_api_token: "$CFTOKEN"
auth:
type: password
password: "$AUTH"
obfs:
type: salamander
salamander:
password: "$OBFS"
masquerade:
type: proxy
proxy:
url: "https://www.microsoft.com/"
rewriteHost: true
EOF
设置权限:
chown root:hysteria /etc/hysteria/config.yaml chmod 640 /etc/hysteria/config.yaml unset CFTOKEN
注意:tls: 与 acme: 只能选择一个,不能同时存在。
启动服务:
systemctl enable --now hysteria-server.service sleep 5 systemctl status hysteria-server.service --no-pager -l journalctl -u hysteria-server.service --since "5 minutes ago" --no-pager
成功时应看到:
server up and running maintenance started background certificate maintenance
这表示 Let’s Encrypt 公网证书已经获取,后台自动维护也已经启动。
八、开启端口跳跃
Hysteria2 Linux 服务端支持直接在 listen 中填写范围。它实际监听范围中的第一个端口,并用 iptables 或 nftables 将其余 UDP 端口重定向到第一个端口。
修改配置:
sed -i 's/^listen:.*/listen: ":20000-21000"/' \ /etc/hysteria/config.yaml
部分系统中,Hysteria 用户虽然有 CAP_NET_ADMIN,但无法创建 /run/xtables.lock。可以提前设置持久化锁文件权限:
touch /run/xtables.lock chown hysteria:hysteria /run/xtables.lock chmod 660 /run/xtables.lock printf '%s\n' \ 'f /run/xtables.lock 0660 hysteria hysteria -' \ > /etc/tmpfiles.d/hysteria-xtables.conf systemd-tmpfiles --create /etc/tmpfiles.d/hysteria-xtables.conf
重启并检查:
systemctl restart hysteria-server.service sleep 3 systemctl status hysteria-server.service --no-pager -l ss -lnup 'sport = :20000' iptables -t nat -S | grep HYSTERIA ip6tables -t nat -S | grep HYSTERIA
正常情况下可以看到:
UDP 20000 由 hysteria 监听 UDP 20001-21000 被重定向到 20000
九、防火墙和云安全组
如果系统本机 INPUT 默认策略为 ACCEPT,且没有 UDP DROP/REJECT 规则,本机通常不需要额外放行:
iptables -S INPUT ip6tables -S INPUT
如果使用 UFW:
ufw allow 20000:21000/udp ufw status
如果使用 firewalld:
firewall-cmd --permanent --add-port=20000-21000/udp firewall-cmd --reload
VPS 服务商后台若有安全组、云防火墙或入站规则,也必须放行:
协议:UDP 端口:20000-21000 来源:按需要设置;测试阶段可使用 0.0.0.0/0
不要为了省事执行 iptables -F,这可能破坏 Docker、3x-ui、Fail2ban 或其他服务的规则。
十、v2rayN 客户端配置
使用较新的 v2rayN,并为 Hysteria2 节点选择 sing_box 内核。
进入:
服务器 → 添加 Hysteria2 服务器
填写:
| 字段 | 内容 |
|---|---|
| 别名 | 自定义,例如 HY2-VPS |
| 地址 | h2.example.com |
| 端口 | 20000 |
| 密码 | AUTH 的值 |
| 跳跃端口范围 | 20000-21000 |
| 端口跳跃间隔 | 30s |
| 最大带宽 Up/Down | 留空,先使用默认拥塞控制 |
| Finalmask | 留空 |
| TLS | tls |
| SNI | h2.example.com |
| Fingerprint | 留空 |
| ALPN | 留空 |
| 跳过证书验证 | Off |
| 固定证书 | 不设置 |
| 混淆密码 | OBFS 的值 |
| Gecko 包大小 | 保持 0 |
| Realm URI | 留空 |
| 内核 | sing_box |
两个密码不要填反:
password → AUTH obfs password → OBFS
如果使用 Base64 密码,末尾 = 是密码的一部分,不能删除;手动填写时不要包含 AUTH= 或 OBFS= 变量名。
保存后将节点设为活动服务器,测试“真连接延迟”,再开启系统代理访问网页。
十一、证书为什么必须用 Let’s Encrypt
Cloudflare 提供的 15 年 Origin CA 通配符证书只用于“Cloudflare 边缘节点到源站”这段链路。Windows、浏览器和 sing-box 默认不信任 Cloudflare Origin CA。
当 DNS 保持灰云、客户端直接连接 VPS 时,Origin CA 证书会出现:
x509: certificate signed by unknown authority
不要长期打开 allowInsecure 绕过验证。正确做法是使用 Let’s Encrypt、ZeroSSL 等客户端默认信任的公网 CA。本文的 Cloudflare DNS-01 方案不占用 TCP 80/443,适合 Caddy 已经运行的网站服务器。
十二、常见故障排查
1. Cloudflare 返回 Invalid API Token
日志示例:
HTTP 403 Code: 9109 Message: Invalid access token
检查:
- 是否已经在摘要页点击“创建令牌”;
- 是否复制了下一页只显示一次的完整 Token;
- 是否误用了 Global API Key、Zone ID 或 Origin CA Key;
- Token 是否包含完整的 cfut_ 前缀;
- Shell 变量是否写成了 CFTOKEN,而不是带转义符的错误形式。
使用 Cloudflare verify API 验证,只有 success:true 才继续。
2. xtables.lock Permission denied
日志:
can't open lock file /run/xtables.lock: Permission denied
执行本文第八节的 tmpfiles.d 修复命令,然后重启服务。
3. iptables Chain already exists
如果 ACME 失败发生在创建端口跳跃规则之后,进程异常退出可能留下 IPv4/IPv6 链。先停止服务,再只清理名称以 HYSTERIA-PR- 开头的链:
systemctl stop hysteria-server.service
for FW in iptables ip6tables; do
for CHAIN in $($FW -t nat -S 2>/dev/null |
awk '/^-N HYSTERIA-PR-/{print $2}'); do
$FW -t nat -D PREROUTING \
-p udp -m udp --dport 20001:21000 \
-j "$CHAIN" 2>/dev/null || true
$FW -t nat -D OUTPUT \
-p udp -m udp --dport 20001:21000 \
-j "$CHAIN" 2>/dev/null || true
$FW -t nat -F "$CHAIN" 2>/dev/null || true
$FW -t nat -X "$CHAIN" 2>/dev/null || true
done
done
确认没有遗留后再启动:
iptables -t nat -S | grep HYSTERIA || echo 'IPv4 已清理' ip6tables -t nat -S | grep HYSTERIA || echo 'IPv6 已清理' systemctl start hysteria-server.service
4. 客户端能发包但没有连接
安装抓包工具:
apt-get install -y tcpdump
观察端口范围:
tcpdump -ni any 'udp portrange 20000-21000'
- 完全没有数据:检查 DNS、云安全组、本地网络;
- 有 UDP 数据但服务端没有认证日志:重点检查 OBFS;
- 服务端提示认证失败:检查 AUTH;
- 客户端提示 x509:检查证书类型、SNI 和系统时间。
5. 如何确认 DNS 不是橙云
Windows:
nslookup h2.example.com
VPS:
curl -4s https://api.ipify.org echo
解析结果必须是 VPS 公网 IP,而不是 Cloudflare Anycast IP。
十三、开机启动和自动续签
确认开机启动:
systemctl is-enabled hysteria-server.service systemctl is-active hysteria-server.service
预期:
enabled active
Hysteria2 会在后台维护 ACME 证书。只要 Cloudflare Token 有效、DNS 权限保留、服务器可以访问 Cloudflare 和 Let’s Encrypt,就会在证书临近到期时自动续签。
查看证书维护日志:
journalctl -u hysteria-server.service | grep -Ei 'certificate|renew|acme|maintenance'
不要删除当前有效的 Cloudflare Token。可以删除测试过程中创建的无效或重复 Token。
十四、最终检查清单
- h2.example.com 解析到 VPS 公网 IP;
- Cloudflare 为灰云,仅 DNS;
- Cloudflare API Token 为 active,权限限制到单一区域;
- Hysteria2 状态为 active (running);
- 日志包含 server up and running;
- UDP 20000 正在监听;
- IPv4/IPv6 跳跃规则存在;
- 云安全组放行 UDP 20000-21000;
- v2rayN 使用 sing-box;
- AUTH、OBFS、SNI 和端口范围一致;
- allowInsecure 保持关闭;
- Cloudflare Token 未被删除。
总结
本文方案不要求停用现有 Caddy 或 3x-ui,也不需要占用 TCP 80/443。核心思路是:
- Cloudflare 灰云只负责 DNS;
- Cloudflare API Token 只负责 DNS-01 验证;
- Let’s Encrypt 提供客户端可信证书;
- Hysteria2 使用独立 UDP 范围完成端口跳跃;
- v2rayN 使用 sing-box 连接。
部署成功后,日志中的:
server up and running
就是服务端正常运行的关键标志。







