朝まだき 頁の奥で 文字めざむ
Cloudflare DNS
└─ 公网 TCP 443:Xray / Reality
├─ 有效 VLESS 客户端 → 正常代理
└─ 普通 HTTPS 握手 → 127.0.0.1:8443(Nginx)
├─ proxy.example.com → 静态网站,用于 Target
├─ panel.example.com → 127.0.0.1:20462(3x-ui)
└─ sub.example.com → 127.0.0.1:2096(订阅,可选)
公网 TCP 80:Nginx,提供 HTTP 和 Let's Encrypt 验证sudo apt update
sudo apt install -y nginx certbot
sudo mkdir -p /var/www/reality-site
printf '<!doctype html><html><head><meta charset="utf-8"><title>Home</title></head><body><h1>Hello</h1></body></html>\n' | sudo tee /var/www/reality-site/index.htmlserver {
listen 80;
server_name proxy.example.com panel.example.com sub.example.com;
root /var/www/reality-site;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}sudo certbot certonly --webroot -w /var/www/reality-site -d proxy.example.com
sudo certbot certonly --webroot -w /var/www/reality-site -d panel.example.com
# 准备使用独立订阅域名时再申请:
sudo certbot certonly --webroot -w /var/www/reality-site -d sub.example.com# 普通网站
server {
listen 127.0.0.1:8443 ssl;
server_name proxy.example.com;
ssl_certificate /etc/letsencrypt/live/proxy.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proxy.example.com/privkey.pem;
root /var/www/reality-site;
index index.html;
location / { try_files $uri $uri/ =404; }
}
# 3x-ui 管理面板
server {
listen 127.0.0.1:8443 ssl;
server_name panel.example.com;
ssl_certificate /etc/letsencrypt/live/panel.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/panel.example.com/privkey.pem;
location / {
proxy_pass https://127.0.0.1:20462;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
# 仅适用于此处可信的本机上游,后续应改进证书校验
proxy_ssl_verify off;
}
}# 直接检查 Nginx HTTPS
curl -kI --noproxy '*' \
--resolve proxy.example.com:8443:127.0.0.1 \
https://proxy.example.com:8443/
# 检查 Xray 443 → Nginx 8443 的完整回落
curl -kI --noproxy '*' \
--resolve proxy.example.com:443:127.0.0.1 \
https://proxy.example.com/# 3x-ui 本机上游
curl -ksS --noproxy '*' https://127.0.0.1:20462/ | grep -i '<title>'
# Nginx 8443
curl -ksS --noproxy '*' \
--resolve panel.example.com:8443:127.0.0.1 \
https://panel.example.com:8443/ | grep -i '<title>'
# 经 Xray 443 访问
curl -ksS --noproxy '*' \
--resolve panel.example.com:443:127.0.0.1 \
https://panel.example.com/ | grep -i '<title>'sudo python3 - <<'PY'
import sqlite3
con = sqlite3.connect('/etc/x-ui/x-ui.db')
keys = ('subPath', 'subJsonPath', 'subClashPath')
for key in keys:
row = con.execute('SELECT value FROM settings WHERE key=?', (key,)).fetchone()
print(f'{key}: {row[0] if row else "<missing>"}')
con.close()
PY
前言
参考 LINUX DO 的 VLESS + Reality 教程,在一台 Ubuntu VPS 上部署了自己的代理节点。
本文只保留关键操作和部分我搭建时遇到的问题,有些解决方法只为方便,会有一些不足。
一、最终架构
这里的关键是端口分工:443 始终由 Xray 占用;Nginx 只在本机的 8443 上提供 HTTPS,不与 Xray 争抢 443。管理面板和订阅服务由 Nginx 转发到各自的本机端口。
Cloudflare 采用 DNS only(灰云)。这意味着域名解析出 VPS 的真实 IP,原生 Reality TCP 流量不会经过 Cloudflare 的普通 HTTP 代理。
二、Cloudflare 和基础检查
在 Cloudflare 创建三条 A 记录,指向同一台 VPS 的公网 IPv4(如只部署前两个服务,sub 可以稍后添加):
主机名
用途
代理状态
`proxy.example.com`
Reality + 真实网站
DNS only
`panel.example.com`
3x-ui 后台
DNS only
`sub.example.com`
客户端订阅,可选
DNS only
检查解析和已有监听:
如果 443 已经由 Xray 监听,**不要再给 Nginx 配置公网 listen 443 ssl;**。
三、部署真实网站并申请证书
Ubuntu 上安装组件并准备静态网页:
为 HTTP 80 创建站点(文件 /etc/nginx/sites-available/reality-http):
启用后验证语法:
之后使用 webroot 方式申请证书,避免 Certbot 修改 HTTPS 监听端口,与 Xray 的 443 冲突:
成功后,证书路径按实际域名填写,例如 /etc/letsencrypt/live/proxy.example.com/fullchain.pem。本次实践曾因为把示例域名 node.example.com 留在 Nginx 配置里,导致 nginx -t 报找不到证书文件。
四、让 Nginx 在本机 8443 提供 HTTPS
创建 /etc/nginx/sites-available/reality-https,先写两个站点;sub 站点稍后按需增加:
启用并检查:
如果已经存在自己的 reality-site、panel-site,应修改现有站点文件,而不是重复创建相同 listen + server_name 的配置。
五、3x-ui Reality 设置:保留 443,只调整回落目标
在原有 VLESS + Reality 入站中检查:
项目
设置
入站端口
`443`
传输
TCP / RAW
安全
Reality
Target / Dest
`127.0.0.1:8443`
SNI / Server Names
`proxy.example.com`
Flow
`xtls-rprx-vision`
客户端指纹
`chrome`
Xver
`0`
UUID、公私钥、Short ID 保持原值;客户端的 servername 应与服务端配置一致。节点地址可保留 VPS IP,也可使用 proxy.example.com,这和订阅地址是否使用域名是两件事。
3x-ui 的 Target 检测器有时会提示 blocked private/internal address 127.0.0.1。这只是检测工具拒绝扫描回环地址,不能据此判断 Xray 本身不支持回落。以实际 HTTPS 测试为准。
我这次第二条测试得到了 HTTP/1.1 200 OK,响应服务器是 Nginx,证书域名正确,证明 Xray → Nginx 的回落链路确实工作。-k 只用于定位握手问题;正式验收还应在可信客户端上不带 -k 验证证书。
六、让 3x-ui 后台直接使用 panel 域名
本次 3x-ui 后台使用自身 HTTPS,内部端口是 20462。为了使用 https://panel.example.com/,面板的 URL 基础路径(webBasePath) 设置为 /,Nginx 的反向代理也必须是 location /。
排查顺序是三层分别测,避免一上来就改 Xray:
三处都应看到类似 <title>Sign in</title>。如果需要保留随机路径增强安全,也可以继续用长路径,并保持面板与 Nginx 配置一致。
七、订阅域名:单独转发,三种路径不能冲突
订阅服务与面板服务不同:本次订阅端口为 2096,默认可在 3x-ui 的「面板设置 → 订阅」中配置。订阅的三个基础路径应彼此不同,例如:
类型
配置键
示例路径
普通订阅
`subPath`
`/sub/`
JSON 订阅
`subJsonPath`
`/json/`
Clash / Mihomo
`subClashPath`
`/clash/`
对应订阅地址形如 https://sub.example.com/clash/<SubID>。SubID 是客户端专属标识,不能为了精简 URL 将它删掉,也不应公开。
若上游 2096 没有启用自身 TLS,可在 Nginx 添加独立的 sub.example.com HTTPS 站点(同样监听本机 8443、使用该域名证书),在 location / 内使用:
如 2096 已配置自身 HTTPS,则上游协议应对应改为 https://。先确认实际订阅路径、Host 限制和上游协议,再测试实际订阅更新。本文已确认路径冲突引起服务崩溃,但订阅域名的最终更新成功状态尚未经过实测确认,不能当作已验收。
八、最值得记住的一次故障:订阅路由冲突导致 3x-ui 无法启动
我在修改 Clash 订阅路径后,3x-ui 3.10.2 直接启动失败,systemctl 报 status=2,日志里有:
这不是域名解析或 SSL 错误,而是 Gin 框架发现重复的订阅路由。最可能的原因是多个订阅路径重叠,尤其是把某些路径配置成同一个 /。
排查与恢复的稳妥思路:停止服务、备份数据库、只检查并修正订阅路径配置,不清空整个面板。
确认数据库中的设置(无需额外安装 sqlite3 CLI):
如果这些路径发生冲突,可在已有数据库备份、并确认配置键存在的情况下分别改为 /sub/、/json/、/clash/,然后再启动检查:
这里记录的是确认过的错误信息和有依据的修复方法;截至本文整理时,没有收到修复后 active (running) 的最终回报,不将其写成已经恢复成功。
九、收尾:端口、安全和维护
本次架构中,公网防火墙通常只需放行 22(SSH)、80(HTTP/证书验证)、443(Reality/HTTPS)。内部的 8443 无须公网放行,面板 20462 和订阅 2096 在反向代理验证成功后也无需暴露公网。删除不认识的旧端口前,先查服务是否仍在使用。
额外建议:管理后台启用 2FA,尽量把管理和订阅监听 IP 收敛到 127.0.0.1,限制管理来源,并保存 3x-ui 数据库、证书和 Nginx 配置的备份。关闭随机路径后,域名简洁不等于管理面板安全。使用自建网站作为 Reality 的 TLS 回落目标,也不意味着流量具有绝对不可识别性。
十、总结
这次实践真正解决的是三类问题:
做任何变更前先备份,修改后按“本机上游 → Nginx 8443 → Xray 443 → 公网浏览器”逐层验证,比一次修改所有配置更可靠。
参考资料