✧ 君と集まって星座になれたら ✦ ROKUTOSEI
7725 字
39 分钟
[弃用] 服务端:单端口五合一节点配置速查
更新于 2026-09-11

已彻底转向 Xray-core,本文不再维护,请自行判断是否过时

TIP

观前提醒

本文仅给出三份重要的配置示例,阅读需具备相关知识

如果你是初次搭建,建议查看完整版

本教程基于 Xray 服务端 + Caddy 反代 + mihomo 客户端 构建

本教程几乎全程手敲命令,不借助 3x-ui 此类面板,需要一定耐心

如果想一键部署,很遗憾,本文不适合你

XHTTP 五合一指的是在同一台 VPS 的 443 端口实现了五个出站节点:

点击查看 适用场景

(此处部分引用 @Benjamin1919 大佬的教程原文案)

出站 1:XTLS(Vision) + REALITY 直连

  • 客户端 GUI 不支持 XHTTP
  • 不想开启 TCP 多路复用
  • 想要尽可能低的延迟与尽可能高的上下行速度

出站 2:XHTTP + REALITY 直连

  • VPS 去程和回程的线路都比较好
  • 想要尽可能低的延迟与尽可能高的上下行速度

出站 3:上行 XHTTP + TLS(ECH) + CDN | 下行 XHTTP + REALITY(上下行不同 SNI)

  • 去程(客户端 → VPS)线路较差,回程(VPS → 客户端)线路较好
  • 日常使用更偏重下载,很少上传
  • 隐藏上行 SNI 防探测封锁

出站 4:XHTTP + TLS(ECH) 过 CDN

  • VPS 去程和回程的线路都很差
  • 不希望 VPS 的 IP 暴露在公网
  • VPS 的 IP 已被墙
  • 隐藏 SNI 防探测封锁

出站 5:上行 XHTTP + REALITY | 下行 XHTTP + TLS(ECH) + CDN(上下行不同 SNI)

  • 去程(客户端 → VPS)线路较好,回程(VPS → 客户端)线路较差
  • 日常使用更偏重上传,很少下载
  • 隐藏下行 SNI 防探测封锁

目录

1. 准备工作#

在开始之前,请准备好以下内容:

  1. 一个个人域名
  2. 一个通过支付验证的 Cloudflare 账号
  3. 一台拥有 root 完全访问权限的 VPS
NOTE

为避免纠纷,本文不提供任何 VPS 商家推荐或购买指导,请自行选择。

本方案属于 Xray 前置、REALITY 回落到本机 Caddy,不需要配置 limitFallbackUpload,

本文中的 <VPS_IP>、<YOUR_USER>、<YOUR_DOMAIN> 均为占位符,实际操作时请替换为你的真实 IP、用户名和域名。

SSH 端口以 <SSH_PORT> 表示(示例为 54321),请根据实际部署修改。


2. Cloudflare 配置#

2.1 添加 DNS 记录#

将域名托管到 Cloudflare,添加 DNS 解析记录:

记录类型子域名值小黄云(Proxy)用途
Acdn.<YOUR_DOMAIN>VPS IPv4开启CDN 回源
AREALITY.<YOUR_DOMAIN>VPS IPv4关闭REALITY 握手直连

如果需要双栈,可额外添加 AAAA 记录,可复用同一个子域名。

2.2 SSL/TLS 设置#

面板左侧 -> SSL/TLS -> 概述 -> 配置 -> 加密模式和源连接设置 -> 加密模式设置为 完全(严格)

面板左侧 -> SSL/TLS -> 边缘证书 -> 开启 TLS 1.3

面板左侧 -> 网络 -> 开启 gRPC

2.3 缓存规则设置#

为避免 XHTTP 请求被缓存,创建缓存规则:

面板左侧 -> 缓存 -> Cache Rules -> 创建缓存规则:

  • 名称:随意
  • 如果传入请求匹配… -> 选择 自定义筛选表达式 -> 点击 编辑表达式
  • 输入(替换为你的域名和路径):
(http.host eq "cdn.<YOUR_DOMAIN>") or (http.request.uri.path contains "/<YOUR_XHTTP_PATH>")
  • 缓存资格 → 设置为 绕过缓存

3. 安装 Xray-core 与 Caddy#

3.1 安装 Xray-core#

通过官方脚本裸装(Bare-metal)并使用 systemd 管理:

推荐安装 Pre-release 版本,目前 Stable 版本还停留在 v26.3.27,新版本包含 XHTTP 修复和功能修复等

发版策略说明

RPRX(Owner):

忘公开说明一下了,关于 Xray-core 的发版策略, pre-release 有可能是实际意义上的 stable,而 latest 也不等于“没有 bug”

没标 latest 通常是因为我觉得下个 stable 该包含什么但它还没有包含,或者单纯懒得写 release notes 那就算了下次吧,感觉比写代码都麻烦

via https://t.me/projectXray/5113846

mihomo 客户端兼容性说明

Xray-core v26.7.11 增加了 REALITY 最低客户端版本校验(minClientVer),默认要求客户端版本 ≥ 26.3.27 由于 mihomo 底层硬编码了版本号 1.8.2,两者不兼容时将导致 REALITY 握手失败

本教程的服务端配置模板已在 realitySettings 中显式添加 "minClientVer": "1.8.2" 以绕过此限制,安装最新版 Xray-core 即可正常工作

具体情况请见相关 Issue / PR:

Terminal window
sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install --beta

如果你依然偏向 Stable,把命令的 --beta 去掉即为下载“正式版”

安装完成时会默认启动服务,需要先暂停:

Terminal window
sudo systemctl stop xray

3.2 生成凭据#

生成 UUID(需要两组,分别用于 Vision 和 XHTTP):

Terminal window
# 执行两次,得到两个不同的 UUID
xray uuid
xray uuid

分别记为 UUID_01 和 UUID_02,保存待用。

生成 REALITY 密钥对:

Terminal window
xray x25519

保存输出的 Private key 和 Public key,私钥填在服务端的 config.json,公钥填在本地客户端里。

生成 shortID:

Terminal window
openssl rand -hex 8

3.3 写入 Xray 配置文件#

TIP

请在本地的文本编辑器,将以下代码块中的占位符替换为你自己的真实信息。替换完成后,粘贴到 VPS 终端中并回车

NOTE

占位符说明:

占位符替换为
UUID_01第一组 UUID(Vision 用户)
UUID_02第二组 UUID(XHTTP 用户)
YOUR_REALITY_PRIVATE_KEY刚才生成的 Private key
YOUR_SHORT_ID刚才生成的 shortID
reality.example.com你的 REALITY 子域名(小黄云关)
cdn.example.com你的 CDN 子域名(小黄云开)
/your-xhttp-path自定义路径
"minClientVer": "1.8.2"保持默认,不要改
Terminal window
sudo tee /usr/local/etc/xray/config.json >/dev/null <<'EOF'
{
"log": {
"loglevel": "info" // 调试完成后可改为 "warning"
},
"inbounds": [
{
"listen": "0.0.0.0", // 如果需要 IPv6,改为监听 "::"
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "UUID_01", // 对应客户端出站 1
"level": 0,
"email": "vision-user",
"flow": "xtls-rprx-vision"
},
{
"id": "UUID_02", // 对应客户端出站 2/3/4/5
"level": 0,
"email": "xhttp-user"
}
],
"decryption": "none",
"fallbacks": [
{
"dest": "/run/xray/xhttp_in.sock",
// 处理“直连”的 XHTTP 请求 (对应客户端出站 2/3/5 的直连部分)
// 当 Reality 识别出 VLESS 协议但没有 flow 时,直接丢给内部 XHTTP Socket
"xver": 0
}
]
},
"streamSettings": {
"network": "raw",
"security": "reality",
"realitySettings": {
"show": false,
"target": "/run/caddy/tls_gate.sock",
// 处理“CDN 转发”或“普通 HTTPS”请求 (对应客户端出站 3/4/5 的 CDN 部分)
// 任何 Reality 不识别的流量都丢给 Caddy 处理
"xver": 0,
"serverNames": [
"reality.example.com",
"cdn.example.com"
],
"privateKey": "YOUR_REALITY_PRIVATE_KEY",
"shortIds": ["YOUR_SHORT_ID"],
"minClientVer": "1.8.2"
},
"sockopt": {
"tcpFastOpen": true,
"tcpcongestion": "bbr",
"tcpMptcp": true,
"tcpNoDelay": true
}
},
"tag": "VISION+REALITY",
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"metadataOnly": false,
"routeOnly": true
}
},
{
"listen": "/run/xray/xhttp_in.sock",
"protocol": "vless",
"settings": {
"clients": [
{
"id": "UUID_02",
"level": 0,
"email": "xhttp-user"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"host": "",
"path": "/your-xhttp-path",
"mode": "auto",
"extra": {
"noSSEHeader": false,
"scMaxEachPostBytes": 1000000,
"scMaxBufferedPosts": 30,
"xPaddingBytes": "100-1000",
"xPaddingObfsMode": true,
"xPaddingMethod": "tokenish", // 生成一个随机的 Base62 标记
"xPaddingPlacement": "queryInHeader", // 填充嵌入 Referer URL query(CF 兼容)
"xPaddingKey": "t", // URL query 参数名
"xPaddingHeader": "Referer",
"sessionIDPlacement": "path",
"sessionIDKey": "SID",
"sessionIDTable": "Base62",
"sessionIDLength": "12-20"
}
},
"sockopt": {
"tcpFastOpen": true,
"acceptProxyProtocol": false,
"tcpcongestion": "bbr",
"tcpMptcp": true,
"tcpNoDelay": true
}
},
"tag": "XHTTP_IN",
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"metadataOnly": false,
"routeOnly": true
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct",
"settings": {}
},
{
"protocol": "blackhole",
"tag": "blocked",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "blocked"
}
]
}
}
EOF

geoip:private blocked 在路由层阻断

Xray v26.7.11 新增的 defaultBlockPrivateRule 在 freedom 出站内部阻断

两者功能重叠,保留作为纵深防御

3.4 安装 Caddy#

使用 Caddy 官方针对 Debian 的软件源安装:

Terminal window
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
# 导入 GPG 密钥
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# 添加官方软件源
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# 更新并安装
sudo apt update
sudo apt install caddy -y

3.5 编写 Caddyfile#

TIP

同理,请在本地编辑器中替换其中的占位符,替换后粘贴整段代码块至终端回车执行

Terminal window
sudo tee /etc/caddy/Caddyfile >/dev/null <<'EOF'
# 1. REALITY 域名区块(专供 REALITY 握手与伪装网页)
# 记得换成你自己的 REALITY 子域名!!!
reality.example.com {
# 监听 Xray REALITY 回落的 Unix Socket
bind unix//run/caddy/tls_gate.sock
# 引用 acme.sh 申请的 Let's Encrypt 证书与私钥
tls /etc/caddy/certs/reality.crt /etc/caddy/certs/reality.key
log {
output file /var/log/caddy/reality_access.log {
roll_size 10mib
roll_keep 7
roll_keep_for 168h
}
}
# 处理所有其他访问
# 外部扫描器或浏览器访问,直接给出伪装网页
handle {
# 伪装网页路径:/var/www/html/index.html
root * /var/www/html
file_server
}
}
# 2. CDN 域名区块(处理从 Xray 回落的 CDN 代理流量)
# 记得换成你自己的 CDN 子域名!!!
cdn.example.com {
bind unix//run/caddy/tls_gate.sock
# 申请的是多域名合并证书,引用相同的证书文件即可
tls /etc/caddy/certs/reality.crt /etc/caddy/certs/reality.key
log {
output file /var/log/caddy/cdn_access.log {
roll_size 10mib
roll_keep 7
roll_keep_for 168h
}
}
# 捕获套 CDN 的出站(出站 3/4/5)传来的合法的 XHTTP 流量
# 记得换成你自己的 XHTTP 路径!!!
# 记得换成你自己的 XHTTP 路径!!!
# 记得换成你自己的 XHTTP 路径!!!
handle /your-xhttp-path/* {
reverse_proxy unix//run/xray/xhttp_in.sock {
transport http {
versions 2
}
flush_interval -1
}
}
# 可选:探针探活路径
handle /status {
respond "ok" 200
}
handle /ping {
respond "ok" 200
}
# 对于扫描器,直接重定向到伪装网页防扫描
# 记得换成你自己的 REALITY 子域名!!!
handle {
redir https://reality.example.com{uri}
}
}
EOF

请务必仔细检查你的 handle 块配置,路径末尾的斜杠和星号 /* 不能省略

XHTTP 流量请求的 URL 是动态长随机混淆参数的

如果前缀不通配,Caddy 就会将其视为未知的外部扫描探测,返回 302 伪装页

附:简单伪装导航页

如果你没有现成的网站,可以使用下面这段单文件 HTML 代码

但是后期建议让 GenAI 丰富一下,换成个人博客、OpenList 什么的

将以下代码块整体复制到终端回车执行,即可一键生成伪装站点:

Terminal window
sudo mkdir -p /var/www/html
sudo tee /var/www/html/index.html >/dev/null <<'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tech Hub</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(-45deg, #e0e7ff, #f3e8ff, #dbeafe, #ede9fe);
background-size: 400% 400%;
animation: gradientBG 18s ease infinite;
}
@keyframes gradientBG {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
padding: 2rem;
width: 100%;
max-width: 720px;
}
.title {
font-size: clamp(2.4rem, 6vw, 3.6rem);
font-weight: 600;
color: #1e293b;
letter-spacing: -0.02em;
text-align: center;
}
.sub {
font-size: clamp(0.8rem, 1.5vw, 1rem);
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.2em;
border-bottom: 1px solid #cbd5e1;
padding-bottom: 1rem;
width: 100%;
text-align: center;
}
.nav {
display: flex;
gap: 1.5rem;
padding: 1.5rem 2.5rem;
border-radius: 80px;
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.8);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.06);
flex-wrap: wrap;
justify-content: center;
}
.nav-item {
display: flex;
justify-content: center;
align-items: center;
width: 3.4rem;
height: 3.4rem;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transition: all 0.25s ease;
text-decoration: none;
border: 1px solid rgba(255, 255, 255, 0.6);
}
.nav-item img {
width: 1.6rem;
height: 1.6rem;
transition: transform 0.2s ease;
}
.nav-item:hover {
transform: translateY(-4px);
background: rgba(255, 255, 255, 0.9);
border-color: #94a3b8;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
}
.nav-item:hover img {
transform: scale(1.1);
}
@media (max-width: 600px) {
.wrapper { gap: 1.5rem; padding: 1rem; }
.nav { padding: 1rem 1.5rem; gap: 1rem; border-radius: 2rem; }
.nav-item { width: 2.8rem; height: 2.8rem; }
.nav-item img { width: 1.3rem; height: 1.3rem; }
}
</style>
</head>
<body>
<div class="wrapper">
<h1 class="title">Dev Center</h1>
<div class="sub">Let there be light</div>
<div class="nav">
<a href="https://www.google.com" class="nav-item" title="Google"><img src="https://cdn.simpleicons.org/google/1e293b" alt="Google"></a>
<a href="https://github.com" class="nav-item" title="GitHub"><img src="https://cdn.simpleicons.org/github/1e293b" alt="GitHub"></a>
<a href="https://dash.cloudflare.com" class="nav-item" title="Cloudflare"><img src="https://cdn.simpleicons.org/cloudflare/1e293b" alt="Cloudflare"></a>
<a href="https://hub.docker.com" class="nav-item" title="Docker Hub"><img src="https://cdn.simpleicons.org/docker/1e293b" alt="Docker"></a>
<a href="https://stackoverflow.com" class="nav-item" title="Stack Overflow"><img src="https://cdn.simpleicons.org/stackoverflow/1e293b" alt="Stack Overflow"></a>
<a href="https://www.npmjs.com" class="nav-item" title="npm"><img src="https://cdn.simpleicons.org/npm/1e293b" alt="npm"></a>
<a href="https://gitlab.com" class="nav-item" title="GitLab"><img src="https://cdn.simpleicons.org/gitlab/1e293b" alt="GitLab"></a>
</div>
</div>
</body>
</html>
EOF

3.6 用户组与运行目录设置#

启动 Xray 和 Caddy 前需统一运行用户为 caddy,否则会出现 socket 权限问题:

[Info] transport/internet/tcp: REALITY: failed to dial dest: dial unix /run/caddy/tls_gate.sock: connect: permission denied

进一步,为避免两个服务重启时因 RuntimeDirectory 被 systemd 清理而导致 UDS 套接字文件丢失,我们将双方的套接字放入各自所属服务的运行时目录(Xray 使用 /run/xray/,Caddy 使用 /run/caddy/):

Terminal window
# 统一运行用户为 caddy
sudo mkdir -p /etc/systemd/system/xray.service.d
sudo tee /etc/systemd/system/xray.service.d/override.conf <<EOF
[Service]
User=caddy
EOF
# 为 Caddy 启用独立的运行目录以放套接字
sudo mkdir -p /etc/systemd/system/caddy.service.d
sudo tee /etc/systemd/system/caddy.service.d/override.conf <<EOF
[Service]
User=caddy
RuntimeDirectory=caddy
EOF
# 重新加载 Systemd 配置
sudo systemctl daemon-reload

3.7 申请公有受信任证书#

点击查看:为什么不能用 Caddy 自带的自动申请证书功能
NOTE

Caddy 默认依赖 HTTP-01 或 TLS-ALPN-01 进行 ACME 自动化证书验证,此过程的硬性前置条件是 Web 服务器必须能够直接在操作系统层面接管并监听公网的 80 或 443 端口。

在当前架构中,公网 443 端口由 Xray 独占监听,Caddy 仅绑定于内网的 Unix Socket 充当回落目标。当 Let’s Encrypt 验证集群发起 TLS-ALPN-01 请求时,流量首先到达 Xray 的 REALITY 服务端。由于该请求不具备有效的 XTLS 身份特征,REALITY 服务端会将其视为未知流量,执行透明的双向 TCP 转发(直接回落至 Caddy 的 Unix Socket)。

尽管验证流量最终送达了 Caddy,但证书申请依然会失败。因为 Caddy 的 ACME 模块在处理 TLS-ALPN-01 挑战时,强依赖于标准 443 端口监听器来挂载专用的验证证书。面对来自 Unix Socket 的回落流量,Caddy 的验证上下文无法正确匹配并下发该证书。即便 Caddy 异常响应了临时的 ACME 验证证书,由于 REALITY 服务端需持续抓取目标后端返回的 ServerHello 以缓存真实证书,这种非标准的临时证书将引发 REALITY 内部证书缓存逻辑链异常,导致整个 TLS 握手在链路中被异常阻断。

所以转而使用 acme.sh 脚本动态下发 DNS TXT 记录(即基于 API 交互的 DNS-01 验证),以实现 TLS 证书合规签发。

另外,REALITY 节点需使用真实 IP 直接承载直连流量(CDN Proxy OFF),若部署未受公共根证书信任的 Cloudflare Origin CA 证书,浏览器直接访问伪装站时会触发 TLS 错误。

第一步:获取 Cloudflare API Token

  1. 登录你的 Cloudflare 网页后台;
  2. 展开左边栏 -> 选择 管理账户 -> 账户 API 令牌;
  3. 点击 创建令牌;
  4. 令牌名称自定义;
  5. 权限策略 -> 编辑策略:选择 指定域名,然后下拉框选你的主域名;
  6. 在下方的权限组列表中,点击展开 DNS & Zones 类别
    • 区域 (Zone) - 区域 (Zone) - 勾选读取 (Read)
    • 区域 (Zone) - DNS - 勾选 编辑 (Edit)
  7. 令牌过期时间建议直接选择 “无过期时间”;
  8. 客户端 IP 地址过滤,可以什么都不填,只要你不把 Token 泄露给别人,就是安全的;或者填上运行 acme.sh 脚本的服务器的公网 IP 地址;
  9. 审核令牌 -> 创建令牌;
  10. 接着会给出 账户ID 和 CF_Token,复制并保存

第二步:在 VPS 切换至 root 并安装 acme.sh

为了后续能够全自动在后台无感续签,这一步全程在 root 用户下执行,以免权限不足导致续签后重载 Caddy 失败。

Terminal window
# SSH 登录 VPS 并切换到 root
sudo -i
# 设置你的真实邮箱,并安装 acme.sh
EMAIL="your_email@example.com"
curl https://get.acme.sh | sh -s email="$EMAIL"
# 切换默认颁发机构为 Let's Encrypt
ACME=/root/.acme.sh/acme.sh
$ACME --set-default-ca --server letsencrypt

第三步:隐式导入 Token 并申请证书

为防止 Token 泄露到 .bash_history 中,我们使用静默输入:

Terminal window
# 执行后,直接粘贴你的 Token 并回车(屏幕上不会显示)
read -rsp 'Cloudflare API Token: ' CF_Token
echo
export CF_Token
export CF_Account_ID="你的Account_ID"
# 申请证书 (必须同时包含 Reality 直连域名和 CDN 回源域名,否则 Cloudflare 在“完全(严格)”模式下回源会报 526 证书错误)
$ACME --issue --dns dns_cf -d "reality.example.com" -d "cdn.example.com"

第四步:提取证书并配置自动授权重载

我们需要把证书提取到 Caddy 的配置目录,每次续签后,私钥文件的属主会被重置为 root,这将导致低权限运行的 Caddy 无法读取私钥而崩溃。因此必须在 --reloadcmd 中加入 chown 权限交接命令:

Terminal window
# 创建目录
mkdir -p /etc/caddy/certs
# 提取证书并设定重载命令
# 安装时只需要指定主域名(第一个 -d 域名),acme.sh 会自动关联
$ACME --install-cert -d "reality.example.com" \
--key-file /etc/caddy/certs/reality.key \
--fullchain-file /etc/caddy/certs/reality.crt \
--reloadcmd "chown caddy:caddy /etc/caddy/certs/reality.* && systemctl reload caddy"
NOTE

证书安装后,私钥文件默认权限为 -rw-------(600),仅属主(root)可读写。
若 Caddy 以 caddy 用户运行,需确保其能读取。上述 reloadcmd 中的 chown 已解决属主问题。
若需进一步限制权限,可额外执行 chmod 640 /etc/caddy/certs/reality.key。

TIP

此时报错 Reload error for: reality.example.com / caddy.service is not active 无伤大雅,因为我们在下一步才正式启动应用。

完成后,输入 exit 退出 root 环境,返回普通用户。

3.8 启动服务#

启动前检查
为避免 permission denied 错误,请再次确认以下路径的属主正确:

Terminal window
# Caddy 日志目录
sudo mkdir -p /var/log/caddy
sudo chown -R caddy:caddy /var/log/caddy
sudo chmod 755 /var/log/caddy
# 证书文件
ls -la /etc/caddy/certs/
# 应显示 -rw-r--r-- 1 caddy caddy reality.crt
# 应显示 -rw------- 1 caddy caddy reality.key
# 若属主不是 caddy,请执行修正:
sudo chown -R caddy:caddy /var/log/caddy
sudo chown caddy:caddy /etc/caddy/certs/reality.*
sudo chmod 640 /etc/caddy/certs/reality.key

正式启动:

Terminal window
sudo systemctl enable --now xray caddy

验证状态:

Terminal window
systemctl show xray --property=User,Group
systemctl show caddy --property=User,Group
ls -l /run/xray/ /run/caddy/

理想输出:

Terminal window
User=caddy
Group=
---
User=caddy
Group=caddy
# /run/xray/
srwxr-xr-x 1 caddy caddy 0 ... xhttp_in.sock
-rw-r--r-- 1 caddy caddy 0 ... xhttp_in.sock.lock
# /run/caddy/
s-w------- 1 caddy caddy 0 ... tls_gate.sock

3.9 后续更新#

更新 Xray-core

官方安装脚本支持直接升级,新版本发布后,重新运行安装命令即可:

Terminal window
# 检查是否有新版本
sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ check
# 升级到最新 Pre-release(推荐)
sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install --beta

脚本会自动检测版本号差异、下载新二进制、替换并重启服务。你的 config.json、override.conf、日志等配置文件全部保留,不会被覆盖。

升级后 systemd 主 service 文件会被重写为默认的 User=nobody,但 override.conf(User=caddy)优先级更高,运行时用户不受影响。 若要消除 Special user nobody configured 警告,可手动修复:

Terminal window
sudo sed -i 's/User=nobody/User=caddy/' /etc/systemd/system/xray.service
sudo systemctl daemon-reload

更新 Caddy

通过 apt 更新:

Terminal window
sudo apt update
sudo apt upgrade caddy -y

4. 客户端配置#

4.1 mihomo Client Config#

由于 mihomo GUI 繁多,此处仅给出裸核 config:

mihomo-client-template.yaml
proxies:
# 1. XTLS(Vision) + REALITY 直连
- name: "出站1-XTLS+REALITY"
type: vless
server: "YOUR_VPS_IP"
port: 443
uuid: "YOUR_UUID_01"
encryption: "none"
flow: xtls-rprx-vision
network: tcp
# 以下两项取决于你的实际 UDP 使用场景,不需要请注释或删除
# udp: true # 需要走代理的境外游戏/QUIC/WebRTC 则开启
# packet-encoding: xudp # UDP 编码方式,开启后支持 FullCone NAT(默认即此值)
tls: true
alpn: [h2]
servername: "reality.example.com" # Reality 伪装域名
client-fingerprint: chrome
reality-opts:
public-key: "YOUR_PUB_KEY"
short-id: "YOUR_SHORT_ID"
# 2. XHTTP + REALITY 直连
- name: "出站2-XHTTP+REALITY"
type: vless
server: "YOUR_VPS_IP"
port: 443
uuid: "YOUR_UUID_02"
encryption: "none"
flow: ""
network: xhttp
# udp: true
# packet-encoding: xudp
tls: true
alpn: [h2]
servername: "reality.example.com" # Reality 伪装域名
client-fingerprint: chrome
reality-opts:
public-key: "YOUR_PUB_KEY"
short-id: "YOUR_SHORT_ID"
xhttp-opts:
path: /your-xhttp-path # 必须与服务端配置的 path 一致
mode: auto # Reality 模式下 auto 会自动选择 stream-one
# host: "reality.example.com" # 直连时可省略,默认继承 servername
session-table: "Alphabet" # 自定义 session ID 字符集,降低 CDN 特征识别
session-length: "12-20" # session ID 长度范围(起始值不可为 0,总 ID 空间须 > 21 亿)
session-placement: path # Session ID 放在 URL 路径中(path 模式下 session-key 无效)
x-padding-obfs-mode: true # 启用填充混淆
x-padding-method: tokenish # 随机 Base62 标记,HPACK 编码后精确匹配目标长度
x-padding-placement: queryInHeader # 填充嵌入 Referer URL query 中(CF 兼容)
x-padding-key: "t" # URL query 参数名,伪装成 analytics token
x-padding-header: "Referer" # 填充存放的 HTTP Header 名,默认 Referer(CF 兼容)
sc-stream-up-server-secs: "30-60" # 单连接流控保活心跳间隔,减少 CDN 超时断开
reuse-settings: # 链接复用设置(即XMUX)
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
# 3. 上行 XHTTP + TLS(ECH) + CDN | 下行 XHTTP + REALITY(上下行不同 SNI)
- name: "出站3-CDN上行+XHTTP下行"
type: vless
server: "cdn.example.com" # CDN 域名或优选 IP (上行)
port: 443
uuid: "YOUR_UUID_02"
encryption: "none"
flow: ""
network: xhttp
ech-opts: {enable: true} # 隐藏 SNI
# udp: true
# packet-encoding: xudp
tls: true
alpn: [h3]
servername: "cdn.example.com" # 写你套 CDN 对应的子域名,CDN 通过它回源服务器
client-fingerprint: chrome
skip-cert-verify: false # 强制校验服务端证书,防止中间人攻击
xhttp-opts: # 上行设置
host: "cdn.example.com" # CDN 转发所需的 HTTP Host,与 servername 保持相同
path: /your-xhttp-path
mode: auto # CDN 模式下 auto 会自动选择 stream-up (H2)
session-table: "Alphabet" # 自定义 session ID 字符集,降低 CDN 特征识别
session-length: "12-20" # session ID 长度范围
session-placement: path # Session ID 放在 URL 路径中(path 模式下 session-key 无效)
x-padding-obfs-mode: true # 启用填充混淆
x-padding-method: tokenish # 随机 Base62 标记,HPACK 编码后精确匹配目标长度
x-padding-placement: queryInHeader # 填充嵌入 Referer URL query 中(CF 兼容)
x-padding-key: "t" # URL query 参数名,伪装成 analytics token
x-padding-header: "Referer" # 填充存放的 HTTP Header 名,默认 Referer(CF 兼容)
sc-stream-up-server-secs: "30-60" # 单连接流控保活心跳间隔,减少 CDN 超时断开
reuse-settings: # 上行链接复用设置(即XMUX)
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
download-settings: # 下行设置(Reality 不支持 h3,须显式覆盖为 h2)
alpn: [h2] # 显式指定 h2,覆盖主代理继承来的 h3
server: "<YOUR_VPS_IP>" # 下行地址 (VPS 直连)
port: 443
servername: "reality.example.com" # Reality 伪装域名
reality-opts:
public-key: "YOUR_PUB_KEY"
short-id: "YOUR_SHORT_ID"
# host 继承自外层或省略;mode 强制继承外层
reuse-settings: # 下行链接复用设置(即XMUX)
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
# 4. XHTTP + TLS(ECH) 过 CDN(上下行不分离)
- name: "出站4-CDN上下行"
type: vless
server: "cdn.example.com" # CDN 域名或优选 IP
port: 443
uuid: "YOUR_UUID_02"
encryption: "none"
flow: ""
network: xhttp
ech-opts: {enable: true} # 隐藏 SNI
# udp: true
# packet-encoding: xudp
tls: true
alpn: [h3]
servername: "cdn.example.com" # 写你套 CDN 对应的子域名,CDN 通过它回源服务器
client-fingerprint: chrome
skip-cert-verify: false # 强制校验服务端证书,防止中间人攻击
xhttp-opts:
host: "cdn.example.com" # CDN 转发所需的 HTTP Host,与 servername 保持相同
path: /your-xhttp-path
mode: auto
session-table: "Alphabet" # 自定义 session ID 字符集,降低 CDN 特征识别
session-length: "12-20" # session ID 长度范围
session-placement: path # Session ID 放在 URL 路径中(path 模式下 session-key 无效)
x-padding-obfs-mode: true # 启用填充混淆
x-padding-method: tokenish # 随机 Base62 标记,HPACK 编码后精确匹配目标长度
x-padding-placement: queryInHeader # 填充嵌入 Referer URL query 中(CF 兼容)
x-padding-key: "t" # URL query 参数名,伪装成 analytics token
x-padding-header: "Referer" # 填充存放的 HTTP Header 名,默认 Referer(CF 兼容)
sc-stream-up-server-secs: "30-60" # 单连接流控保活心跳间隔,减少 CDN 超时断开
reuse-settings: # 链接复用设置(即XMUX)
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
# 5. 上行 XHTTP + REALITY | 下行 XHTTP + TLS(ECH) + CDN(上下行不同 SNI)
- name: "出站5-上XHTTP+REALITY下XHTTP+TLS+CDN"
type: vless
server: "YOUR_VPS_IP"
port: 443
uuid: "YOUR_UUID_02"
encryption: "none"
flow: ""
network: xhttp
# udp: true
# packet-encoding: xudp
tls: true
alpn: [h2]
servername: "reality.example.com" # Reality 伪装域名
client-fingerprint: chrome
skip-cert-verify: false # 强制校验服务端证书,防止中间人攻击
reality-opts:
public-key: "YOUR_PUB_KEY"
short-id: "YOUR_SHORT_ID"
xhttp-opts:
host: "cdn.example.com" # CDN 转发所需的 HTTP Host,与 servername 保持相同
path: /your-xhttp-path
mode: auto
session-table: "Alphabet" # 自定义 session ID 字符集,降低 CDN 特征识别
session-length: "12-20" # session ID 长度范围
session-placement: path # Session ID 放在 URL 路径中(path 模式下 session-key 无效)
x-padding-obfs-mode: true # 启用填充混淆
x-padding-method: tokenish # 随机 Base62 标记,HPACK 编码后精确匹配目标长度
x-padding-placement: queryInHeader # 填充嵌入 Referer URL query 中(CF 兼容)
x-padding-key: "t" # URL query 参数名,伪装成 analytics token
x-padding-header: "Referer" # 填充存放的 HTTP Header 名,默认 Referer(CF 兼容)
sc-stream-up-server-secs: "30-60" # 单连接流控保活心跳间隔,减少 CDN 超时断开
reuse-settings: # 上行链接复用设置(即XMUX)
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
download-settings: # 下行设置(CDN 路径,改用 h3)
path: /your-xhttp-path
host: "" # host 继承自外层或省略;mode 强制继承外层
server: "cdn.example.com" # CDN / 优选 IP (下行连接地址)
port: 443
tls: true
alpn: [h3]
servername: "cdn.example.com" # 写你套 CDN 对应的子域名,CDN 通过它回源服务器
client-fingerprint: chrome
skip-cert-verify: false # 强制校验服务端证书,防止中间人攻击
ech-opts: {enable: true} # 隐藏 SNI
reality-opts: { public-key: "" } # 强制清空 Reality 公钥,否则下行连接 CDN 将会携带 Reality 握手
reuse-settings: # 下行链接复用设置(即XMUX)
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
锚点写法
mihomo-client-template-anchors.yaml
# 定义锚点
x-anchors:
# 0. 用户变量 (请在此处统一修改为自己的配置,下方会自动引用)
variables:
vps_ip: &vps_ip "YOUR_VPS_IP"
uuid_01: &uuid_01 "YOUR_UUID_01"
uuid_02: &uuid_02 "YOUR_UUID_02"
reality_domain: &reality_domain "reality.example.com"
cdn_domain: &cdn_domain "cdn.example.com"
xhttp_path: &xhttp_path "/your-xhttp-path"
# 1. Reality 密钥配置
reality-opts: &reality-opts
public-key: "YOUR_PUB_KEY"
short-id: "YOUR_SHORT_ID"
# 2. 链接复用设置参数
reuse-settings: &reuse-settings
max-concurrency: "16-32" # 单连接最大并发请求数(0=不限制)
# max-connections: "3" # 最大连接数(0=不限制)(与 max-concurrency 冲突,二选一)
c-max-reuse-times: "0" # 单连接最大复用次数(0=不限制)
# h-max-request-times: "600-900" # 单连接累计 HTTP 请求数(不严谨,慎用)
h-max-reusable-secs: "1800-3000" # 单连接最长复用时间(秒)
h-keep-alive-period: 0 # H2/H3 保活间隔 (0=默认智能值, -1=关闭, 支持填正数范围但不推荐)
# 3. XHTTP 混淆配置
xhttp-obfs: &xhttp-obfs
path: *xhttp_path
mode: auto
session-table: "Alphabet" # 自定义 session ID 字符集,降低 CDN 特征识别
session-length: "12-20" # session ID 长度范围
session-placement: path # Session ID 放在 URL 路径中
x-padding-obfs-mode: true # 启用填充混淆
x-padding-method: tokenish # 随机 Base62 标记,HPACK 编码后精确匹配目标长度
x-padding-placement: queryInHeader # 填充嵌入 Referer URL query 中
x-padding-key: "t" # URL query 参数名,伪装成 analytics token
x-padding-header: "Referer" # 填充存放的 HTTP Header 名,默认 Referer
sc-stream-up-server-secs: "30-60" # 单连接流控保活心跳间隔,减少 CDN 超时断开
reuse-settings: *reuse-settings
# 4. UDP 配置
udp-opts: &udp-opts
# 以下两项取决于你的实际 UDP 使用场景
udp: false # 境外游戏/QUIC/WebRTC 有需要走代理的则改为 true
# packet-encoding: xudp # UDP 编码方式
# 最终节点配置
proxies:
# 1. XTLS(Vision) + REALITY 直连
- name: "出站1-XTLS+REALITY"
type: vless
server: *vps_ip
port: 443
uuid: *uuid_01
encryption: "none"
flow: xtls-rprx-vision
network: tcp
<<: *udp-opts
tls: true
alpn: [h2]
servername: *reality_domain
client-fingerprint: chrome
reality-opts: *reality-opts
# 2. XHTTP + REALITY 直连
- name: "出站2-XHTTP+REALITY"
type: vless
server: *vps_ip
port: 443
uuid: *uuid_02
encryption: "none"
flow: ""
network: xhttp
<<: *udp-opts
tls: true
alpn: [h2]
servername: *reality_domain
client-fingerprint: chrome
reality-opts: *reality-opts
xhttp-opts: *xhttp-obfs
# 3. 上行 XHTTP + TLS(ECH) + CDN | 下行 XHTTP + REALITY
- name: "出站3-CDN上行+XHTTP下行"
type: vless
server: *cdn_domain
port: 443
uuid: *uuid_02
encryption: "none"
flow: ""
network: xhttp
<<: *udp-opts
tls: true
alpn: [h3]
servername: *cdn_domain
client-fingerprint: chrome
skip-cert-verify: false
ech-opts: {enable: true}
xhttp-opts:
<<: *xhttp-obfs
host: *cdn_domain
download-settings:
server: *vps_ip
port: 443
tls: true
alpn: [h2]
servername: *reality_domain
client-fingerprint: chrome
reality-opts: *reality-opts
reuse-settings: *reuse-settings
# 4. XHTTP + TLS(ECH) 过 CDN(上下行不分离)
- name: "出站4-CDN上下行"
type: vless
server: *cdn_domain
port: 443
uuid: *uuid_02
encryption: "none"
flow: ""
network: xhttp
<<: *udp-opts
tls: true
alpn: [h3]
servername: *cdn_domain
client-fingerprint: chrome
skip-cert-verify: false
ech-opts: {enable: true}
xhttp-opts:
<<: *xhttp-obfs
host: *cdn_domain
# 5. 上行 XHTTP + REALITY | 下行 XHTTP + TLS(ECH) + CDN
- name: "出站5-上XHTTP+REALITY下XHTTP+TLS+CDN"
type: vless
server: *vps_ip
port: 443
uuid: *uuid_02
encryption: "none"
flow: ""
network: xhttp
<<: *udp-opts
tls: true
alpn: [h2]
servername: *reality_domain
client-fingerprint: chrome
skip-cert-verify: false
reality-opts: *reality-opts
xhttp-opts:
<<: *xhttp-obfs
host: *cdn_domain
download-settings:
server: *cdn_domain
port: 443
tls: true
alpn: [h3]
servername: *cdn_domain
client-fingerprint: chrome
skip-cert-verify: false
ech-opts: {enable: true}
path: *xhttp_path
host: ""
reality-opts: { public-key: "" }
reuse-settings: *reuse-settings

4.2 ALPN、ECH 与 XMUX 说明#

ALPN#

  • mihomo v1.19.23:仅支持 h2
  • mihomo v1.19.24+:支持 http/1.1 和 h3,但不支持多协议动态回退,写法如下:
proxies:
- name: xxx
alpn: [h2] # 使用 HTTP/2(默认)
# alpn: [http/1.1] # 强制使用 HTTP/1.1
# alpn: [h3] # 强制使用 HTTP/3。注意:不支持 REALITY,必须 tls: true

ECH#

proxies:
- name: xxx
ech-opts: {enable: true}

Cloudflare 免费版现已默认启用 ECH(Encrypted ClientHello)

针对通过 CDN 回源的出站节点,加上此参数后,mihomo 会自动发起 HTTPS DNS 记录查询,拉取 Cloudflare 下发的 ECH 公钥,并在 TLS 握手时加密 SNI,可以有效防止 GFW 通过嗅探 SNI 来封锁连接,一定程度上提升连接的隐蔽性

NOTE
  1. 只有套了 CDN 的节点才能用
  2. mihomo 需要写支持 DoH 解析的 DNS
  3. 域名需要开启 TLS 1.3

更多详情请查看下面推荐的文章

XMUX(链接复用)#

IMPORTANT

Xray-core 近期的版本演进中(Issue #6376),对 XMUX 的默认策略经历了多次调整(从最初的 16-32,到 1,再到 3)。

  • 本模板中显式写了 max-concurrency: "16-32"。效果是强制锁定在较稳定的多路复用状态,避免因继承内核默认值而导致并发 TCP 连接数突增爆表(仅为个人理解)
  • 如果为了对抗严格的审查环境(像 issue 提到的俄罗斯抗 TSPU 封锁)的话,可改为 max-connections: 3

【完】


友情推荐#

Rokutosei
Rokutosei ✦ 六等星
“君と集まって星座になれたら”
[弃用] 服务端:单端口五合一节点配置速查
https://blog.rokutosei.com/posts/proxy/vlessxhttpproxysimplify/
作者
Rokutosei
发布于
2026-07-14
修改于
2026-09-11
许可协议
CC BY-NC-SA 4.0
✧ 文章目录 ✦
1
1. 准备工作
2
2. Cloudflare 配置
2.1 添加 DNS 记录
2.2 SSL/TLS 设置
2.3 缓存规则设置
3
3. 安装 Xray-core 与 Caddy
3.1 安装 Xray-core
3.2 生成凭据
3.3 写入 Xray 配置文件
3.4 安装 Caddy
3.5 编写 Caddyfile
3.6 用户组与运行目录设置
3.7 申请公有受信任证书
3.8 启动服务
3.9 后续更新
4
4. 客户端配置
4.1 mihomo Client Config
4.2 ALPN、ECH 与 XMUX 说明
5
友情推荐