Xray VLESS + Cloudflare CDN 完整配置教程(VPS 2026)
CDN 模式下客户端连的是 Cloudflare 边缘 IP 而不是 VPS 真实 IP,WebSocket 握手失败最常见的原因是路径不匹配、SSL/TLS 模式选错了或者 Xray 端口没开放。这篇给出 VPS 端配置、Cloudflare DNS 和客户端填法,附带 502 和 WebSocket upgrade failed 的定位表。
VLESS 套 Cloudflare CDN 的数据路径多了一跳:客户端先把 WebSocket 请求发到 Cloudflare 边缘节点,边缘节点解密后再转发到你的 VPS。好处是 Cloudflare 的 IP 地址池不会被干扰,自带基础 DDoS 防护;坏处是多了一跳,延迟增加 30-100ms 不等。适合对延迟不敏感、但需要稳定连接的场景,或者 VPS IP 曾经被记录过需要藏一下。
如果你用的是 VLESS Reality,连 CDN 都不需要。Reality 走的是 TCP raw + 偷证书的路线,不依赖 WebSocket,也不用 Cloudflare 中转。这篇文章默认你已经决定走 CDN 路线——手里有域名、有 VPS、域名 DNS 托管在 Cloudflare、VPS 上 Xray 已装好或准备装。
Cloudflare 端需要改三个地方
打开 Cloudflare Dashboard,选好域名后按顺序检查三个位置。
第一处:DNS 记录。 进入 DNS → 记录 → 添加记录。类型选 A,名称可以用根域名(@)或子域名(比如 cdn、proxy),IPv4 地址填 VPS 的公网 IP,代理状态必须点成橙色云朵(Proxied)。灰色云朵等于直连,CDN 不会介入,跟没配置一样。
保存后用 dig 你的域名 或 nslookup 你的域名 检查一下——返回的 IP 应该是 Cloudflare 的边缘 IP(104.x.x.x、172.x.x.x 段),不是 VPS 的真实 IP。如果返回的是 VPS IP,说明代理没生效,回去检查橙色云朵是不是点上了。
第二处:SSL/TLS 加密模式。 SSL/TLS → Overview → 加密模式选 Full。这是整个配置里踩坑率最高的一步。Flexible 模式听起来最简单,但它强制 Cloudflare 到源站走 HTTP(80 端口),Xray 在 443 端口等的是 WebSocket over TLS 连接,两边对不上,客户端请求直接 502。
Full 模式下 Cloudflare 发起 HTTPS 连接到 VPS 的 443 端口,验证 TLS 握手,但不校验证书签发链。Xray 自签的证书、Cloudflare Origin CA 签的 15 年证书都能过。如果你的 Xray 用的是 Let’s Encrypt 等公共 CA 签发的有效证书,可以开到 Full (Strict),但对个人节点的安全性没有实质提升,反而多一个续签证书的维护点。
第三处:Edge Certificates 确认。 SSL/TLS → Edge Certificates。免费套餐下 Cloudflare 会自动给域名签发和续期 Universal SSL 证书。确认「Always Use HTTPS」是开启状态,这会让 Cloudflare 把所有 HTTP 请求自动 301 到 HTTPS。
这三个设置改完后,Cloudflare 端就完成了。不需要开任何 WebSocket 专项开关——Cloudflare 免费套餐原生支持 WebSocket 代理,不需要额外配置。
Xray 服务端怎么配
下面是 VLESS + WebSocket + TLS 的服务端配置,监听 443 端口。把 /usr/local/etc/xray/config.json 的内容替换成这个结构(UUID 和 path 替换成你自己的值):
{
"inbounds": [
{
"tag": "vless-ws-cdn",
"listen": "0.0.0.0",
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "这里填 xray uuid 命令生成的 UUID",
"flow": ""
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/a1b2c3d4-vw"
},
"tlsSettings": {
"certificates": []
}
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv4"
}
}
]
}
几个关键点解释一下:
- certificates 留空数组:Xray 1.8.0+ 会自动生成自签证书用于 TLS 握手。因为访客端只看到 Cloudflare 边缘节点的证书(Universal SSL),看不到你源站的证书。自签证书足够完成 Cloudflare 到源站的 TLS 握手。不用买证书,不用配 Let’s Encrypt。
- path 不要用常见词:
/ws、/vless、/proxy这类路径被扫描的概率高。用随机长字符串,比如/a1b2c3d4-vw,大小写敏感。path 是 CDN 模式下唯一区分「这是代理请求还是普通 HTTPS 请求」的标识。 - port 443:CDN 模式下 Cloudflare 默认连接源站的 443 端口。如果你用其他端口(比如 8443),需要在 Cloudflare Origin Rules 或 SSL/TLS 自定义源站端口里额外配置。
- flow 留空:Flow Control(xtls-rprx-vision 等)只在 VLESS + XTLS 的 TCP 模式下使用。WebSocket 模式下 flow 必须留空,填写反而导致握手失败。
修改完配置后 systemctl restart xray,用 journalctl -u xray -f 看启动日志。如果出现 failed to listen on 0.0.0.0:443: bind: address already in use,说明 443 端口被 Nginx、Caddy 或旧版 Xray 进程占用了。用 ss -tlnp | grep 443 找到占用的进程,停掉后再重启 Xray。
客户端怎么填
客户端创建 VLESS 节点时的参数如下——注意地址填的是域名,不是 VPS IP。
| 参数 | 值 | 为什么 |
|---|---|---|
| 地址 (address) | 你的域名(如 cdn.example.com) | CDN 模式下客户端必须连域名,让 DNS 解析到 Cloudflare 边缘 IP |
| 端口 (port) | 443 | Cloudflare 代理的默认 HTTPS 端口 |
| 用户 ID (uuid) | 服务端 clients.id 里的 UUID | 保持与服务端一致 |
| 流控 (flow) | 留空 | WebSocket 传输不支持 xtls-rprx-vision |
| 传输协议 (network) | ws | 与 Xray streamSettings.network 一致 |
| 路径 (path) | /a1b2c3d4-vw(与服务端 wsSettings.path 完全一致) | 大小写敏感,必须以 / 开头 |
| 底层传输安全 (security) | tls | 客户端会发起到 Cloudflare 边缘节点的 TLS 连接 |
| SNI (sni/serverName) | 你的域名 | Cloudflare 边缘节点用 SNI 判断这条请求转发给哪个源站 |
常见客户端操作路径:
- v2rayN (Windows):服务器 → 添加 VLESS 服务器 → 手动填写以上参数 → 确定。
- v2rayNG (Android):右上角 + → 手动新建 → VLESS → 填写参数 → 右上角保存。
- NekoBox (跨平台):首页 → 新建配置 → VLESS → 手动填写 → 传输选 ws → TLS 开启 → 保存。
- Shadowrocket (iOS):右上角 + → 类型选 VLESS → 填写 → 传输选 ws → TLS 开启 → 完成。
填完后点连接测试或直接连接。客户端日志中出现 WebSocket connection established 或 connected to server 即表示 CDN 链路通了。浏览器访问 https://api.ipify.org,显示的应该是 VPS 的出口 IP——证明流量走的是「客户端 → Cloudflare → VPS → 公网」这条路径。
VLESS + CDN 和 VLESS Reality 直连怎么选
CDN 和 Reality 不是谁更好的问题,是适用场景完全不重叠。
| 维度 | VLESS + CDN | VLESS Reality 直连 |
|---|---|---|
| 数据路径 | 客户端 → Cloudflare 边缘 → VPS | 客户端 → VPS |
| 额外延迟 | 30-100ms | 0 |
| IP 隐藏 | VPS 真实 IP 完全不暴露给客户端 | VPS IP 直接暴露给客户端 |
| 抗干扰能力 | 强(Cloudflare IP 池大且变动频繁) | 中等(依赖 TLS 指纹伪装) |
| DDoS 防护 | Cloudflare 自带基础防护 | 无(VPS 自身扛) |
| 是否需要域名 | 必须(且 DNS 托管在 Cloudflare) | 不需要(偷取目标站点域名做伪装) |
| 部署复杂度 | 需要配置 DNS、SSL 模式、Xray TLS | 只需 Xray Reality inbound + 一份公钥对 |
| 速度上限 | 受 Cloudflare 边缘节点带宽和路由影响 | 直连受 VPS 线路质量影响 |
| 适合谁 | IP 容易被干扰、需要 DDoS 防护、不在乎延迟 | 延迟敏感、VPS 线路好、不怕暴露 IP |
如果你在国内使用,Cloudflare 到国内客户端的边缘节点路由有时候会绕美国或欧洲,延迟可以到 200ms+。遇到这种情况,先测一下你的本地运营商到 Cloudflare 的 ping——如果本身超过 150ms,CDN 模式就不太合适,改用 Reality 直连或换一条 CN2 GIA 线路的 VPS。
502 Bad Gateway 和 WebSocket 升级失败怎么排
CDN 模式下最快的定位方法是看错误页面是谁返回的。
返回的是 Cloudflare 的 502 页面(底部有 Cloudflare 标志和 Ray ID):说明请求到达了 Cloudflare,但 Cloudflare 连不上你的 VPS。从概率高到低查下面几个点:
| 现象 | 最可能原因 | 动作 |
|---|---|---|
Cloudflare 502,VPS 上 ss -tlnp | grep 443 有 xray 进程 | VPS 防火墙没放行 Cloudflare IP 段 | ufw allow 443/tcp 或 iptables -A INPUT -p tcp --dport 443 -j ACCEPT;不要只放行单个 IP,Cloudflare 边缘 IP 范围很大且动态变化 |
| Cloudflare 502,443 端口没有进程在监听 | Xray 没启动或没监听 443 | systemctl status xray → 如果没在跑先 systemctl restart xray → 看 journalctl -u xray 最后 20 行的报错 |
| Cloudflare 502,Xray 正常跑着 | Cloudflare SSL/TLS 模式设成了 Flexible | SSL/TLS → Overview → 改成 Full |
| Cloudflare 502,时间不稳定(有时能连有时 502) | VPS 防火墙有连接数限制或 fail2ban 误封 Cloudflare IP | 检查 iptables recent 模块或 fail2ban 规则,Cloudflare IP 段不能被限制连接频率 |
返回的是 Xray 拒绝或客户端超时、没有 Cloudflare 502 页面:说明 Cloudflare 到 VPS 的链路通了,问题在 Xray 的参数匹配上。最常见是 path 不匹配——服务端写的是 /a1b2c3d4-vw,客户端漏了前缀斜杠写成了 a1b2c3d4-vw。大小写也必须完全一致。第二个常见原因是 UUID 写错了,服务端和客户端复制的时候少了一个字符或多了换行。
客户端日志显示 WebSocket upgrade failed 但 TCP 连接建立了:Cloudflare 到 VPS 的 TCP 443 端口连接成功了,但路径不对或 Xray 收到的请求头部不完整。浏览器直接访问 https://你的域名/你的path 看返回——如果是 400 Bad Request(来自 Xray),说明路径匹配但请求不是 WebSocket 握手格式(这是正常的,浏览器不是 WebSocket 客户端);如果是 Cloudflare 的 502 页面,说明路径不对,Xray 根本没收到请求。
客户端延迟忽高忽低,偶尔断开重连:大概率是 Cloudflare 边缘节点的路由不稳定。可以在客户端测一下 ping 你的域名,连续的延迟值不应该跳变超过 50ms。如果波动大,在 Cloudflare Dashboard 换一个边缘节点覆盖更好的域名(比如把 A 记录从 @ 改成 cdn,Cloudflare 可能会分配到不同的 IP 段),或者直接切到 Reality 直连。
相关阅读
来源与时间
本文最后查看时间:2026-05-29。操作路径会随客户端版本变化,遇到按钮名称不一致时,优先按同义菜单和官方文档查看。