Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


A VLESS CDN setup puts a CDN edge between your client and an Ubuntu VPS you control: the client connects to your domain at the edge, the CDN forwards one secret path to your origin, and Xray accepts it only on loopback. This guide uses Xray v26.3.27, Nginx as the origin proxy, and Cloudflare as the example CDN, with one transport chosen from WebSocket or XHTTP.[1]
Key Takeaways
- Read the CDN's current terms before building anything; a working tunnel does not make the use permitted.
- Use a strict origin TLS mode such as Cloudflare Full (strict), never a mode that skips certificate validation.
- Keep Xray on
127.0.0.1and let Nginx forward only one secret path per transport.- Treat XHTTP
packet-upas the conservative CDN choice and WebSocket as the simpler, older one.- Separate "the client connected" from "traffic used the CDN": test direct-origin, wrong-path, and wrong-UUID refusals.
This procedure is based on the official sources listed below; a complete end-to-end test has not been performed. Version numbers describe the documentation used, not an author-tested environment. Before you start, define ownership, access, and recovery with the self-hosting planning checklist.
Run this only on a VPS and a domain you own or administer. You need console access that does not depend on the proxy, SSH with strict host-key checking against a verified known_hosts, and a backup of the current Nginx, firewall, and Xray files. Do not borrow someone else's domain, a free subdomain service, or a third-party Worker to host your entry.
A CDN changes who can see and terminate your traffic. The edge decrypts TLS from the client and opens a second TLS connection to your origin, so the CDN operator handles your proxy stream in between. If you only need a dependable daily exit and do not want to maintain certificates, Nginx, and Xray yourself, check whether a managed VPN would save that upkeep before adding another moving part. For how edges and origins relate in general, see how a CDN sits between you and a site.
Cloudflare's service-specific terms reserve the right to disable or limit CDN use that serves video or a disproportionate share of large files without the relevant paid services.[2] A proxy that carries streaming or bulk downloads can fall into that pattern. Read the current terms and plan limits for your account, and keep a direct entry such as a VLESS REALITY server if you need a path that does not depend on the CDN's policy.
Nothing here makes a route unblockable. The CDN can restart edge servers, apply idle timeouts, or change features, and Cloudflare states that releases can terminate open WebSocket connections.[3]
Both transports carry VLESS inside HTTP, so a CDN that proxies HTTP can forward them. They differ in how the stream is framed and in what the CDN must allow.
| Question | VLESS WebSocket TLS | VLESS XHTTP CDN (packet-up) |
|---|---|---|
| How data moves | One upgraded HTTP/1.1 connection | Upload as numbered POST requests, download as a streaming GET |
| CDN requirement | WebSockets enabled for the zone | Plain HTTP proxying; no WebSocket upgrade |
| Project X guidance | Docs recommend moving to XHTTP because WebSocket's ALPN is http/1.1 | packet-up described as the most compatible mode behind CDNs and proxies |
| Origin proxy needs | Upgrade and Connection headers | Unbuffered request and response bodies |
| Main trade-off | Simple and widely supported by clients | Newer; client and server cores must both support it |
Project X's WebSocket page recommends switching to XHTTP to avoid WebSocket's distinctive http/1.1 ALPN.[4] The XHTTP transport entry points to the project's XHTTP discussion, which recommends packet-up when a CDN or reverse proxy will not pass the other modes, and notes that stream-up through Cloudflare needs gRPC support enabled.[5][6] This guide keeps XHTTP at packet-up so the origin can stay a normal HTTP/1.1 proxy. If you already run a direct XHTTP entry, keep it separate from this CDN entry.
Create a DNS record such as proxy.example.com pointing to your VPS and set it to proxied through the CDN. In Cloudflare, set the zone's SSL/TLS mode to Full (strict): the edge then validates your origin certificate, which can come from a public CA or from Cloudflare Origin CA.[7] Do not use Flexible or Full, which either skip origin TLS or accept invalid certificates.
If you choose WebSocket, turn on WebSockets in the zone's Network settings.[3] Install the origin certificate and key at /etc/ssl/vless-cdn/fullchain.pem and /etc/ssl/vless-cdn/privkey.pem, owned by root, with the key at mode 0600.
Limit TCP 443 on the origin to the CDN's published address ranges so that clients cannot skip the edge.[8] Keep your SSH rule and any certificate-renewal path that still needs to reach the server. Re-check those ranges when the CDN updates them; a stale list can break the entry without warning.
Download the Linux 64-bit archive from the official v26.3.27 release, check its release notes and published verification material, and install the binary at /opt/xray-cdn/v26.3.27/xray, root-owned, mode 0755. Generate a UUID with /opt/xray-cdn/v26.3.27/xray uuid and pick a random path segment for each transport. Create a non-login account xray-cdn and save this as /etc/xray-cdn/server.json, mode 0640, root:xray-cdn.[1]
{
"log": {"loglevel": "warning"},
"inbounds": [
{
"tag": "vless-ws",
"listen": "127.0.0.1",
"port": 10001,
"protocol": "vless",
"settings": {"clients": [{"id": "REPLACE_UUID"}], "decryption": "none"},
"streamSettings": {"network": "ws", "wsSettings": {"path": "/ws-REPLACE_SECRET"}}
},
{
"tag": "vless-xhttp",
"listen": "127.0.0.1",
"port": 10002,
"protocol": "vless",
"settings": {"clients": [{"id": "REPLACE_UUID"}], "decryption": "none"},
"streamSettings": {"network": "xhttp", "xhttpSettings": {"path": "/xh-REPLACE_SECRET/", "mode": "packet-up"}}
}
],
"outbounds": [{"tag": "direct", "protocol": "freedom"}]
}
Keep only the inbound for the transport you chose. Neither inbound enables TLS, because TLS ends at Nginx on the same host; that is safe only while both listen on 127.0.0.1. VLESS decryption: none means VLESS adds no encryption of its own. Validate with sudo -u xray-cdn /opt/xray-cdn/v26.3.27/xray run -test -config /etc/xray-cdn/server.json, then run it as a systemd service under the xray-cdn account.
Add a server block for proxy.example.com. Every path other than your secret ones returns 404, so a scanner at the origin sees an ordinary HTTPS site with nothing behind it.
server {
listen 443 ssl;
server_name proxy.example.com;
ssl_certificate /etc/ssl/vless-cdn/fullchain.pem;
ssl_certificate_key /etc/ssl/vless-cdn/privkey.pem;
location = /ws-REPLACE_SECRET {
proxy_pass http://127.0.0.1:10001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 300s;
}
location /xh-REPLACE_SECRET/ {
proxy_pass http://127.0.0.1:10002;
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Host $host;
proxy_read_timeout 300s;
}
location / { return 404; }
}
The WebSocket block passes the Upgrade and Connection headers that a hop-by-hop proxy otherwise drops.[9] The XHTTP block turns off buffering in both directions; with proxy_http_version 1.1, Nginx can pass a request body to Xray as it arrives instead of collecting it first.[10] The XHTTP location uses a prefix match because each request adds its own suffix to the path. Run sudo nginx -t and reload only after it passes.
On an Ubuntu client, install the same Xray version and save a private client.json (mode 0600) for the WebSocket route:
{
"inbounds": [{"listen": "127.0.0.1", "port": 1083, "protocol": "socks", "settings": {"auth": "noauth", "udp": false}}],
"outbounds": [{
"tag": "cdn-ws",
"protocol": "vless",
"settings": {"vnext": [{"address": "proxy.example.com", "port": 443, "users": [{"id": "REPLACE_UUID", "encryption": "none"}]}]},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {"serverName": "proxy.example.com", "allowInsecure": false},
"wsSettings": {"path": "/ws-REPLACE_SECRET"}
}
}]
}
For XHTTP, keep the same outbound and replace the transport fields:
"network": "xhttp",
"xhttpSettings": {"path": "/xh-REPLACE_SECRET/", "mode": "packet-up"}
The address and serverName are your domain, not the origin IP, so the client's TLS session ends at the edge and certificate validation stays on. Run xray run -test -config ./client.json, then start the client in the foreground and point one test application at SOCKS5 127.0.0.1:1083.
A page loading through the proxy only proves that some path works. Check each boundary separately, changing one thing per test and restoring the working file in between.
| Check method | Expected behavior | If it differs |
|---|---|---|
curl --proxy socks5h://127.0.0.1:1083 https://check.example.net/health against your own endpoint | Expected response through the proxy | Read Nginx and Xray logs before changing transport |
dig +short proxy.example.com | Returns CDN edge addresses, not your VPS IP | Record is not proxied; fix DNS before trusting other results |
From an outside host: curl --resolve proxy.example.com:443:ORIGIN_IP https://proxy.example.com/ | Connection times out or is refused | Origin firewall still admits non-CDN clients |
Request https://proxy.example.com/wrong-path through the edge | 404 from Nginx | A broad location block is forwarding too much |
| Client copy with another valid UUID | Proxy request fails | Check that the old client process is stopped and the UUID list is correct |
Client copy with a serverName that is not on the edge certificate | TLS validation fails | Keep allowInsecure: false; never "fix" this by allowing insecure TLS |
Then open the IP address tool in a browser configured for the local SOCKS port. It should show your VPS egress address, because the CDN carries the inbound leg while Xray's freedom outbound leaves from the server. That result covers the browser request only, not DNS, IPv6, or UDP from other apps.
If WebSocket requests fail while ordinary HTTPS through the same edge works, the WebSocket-versus-HTTPS diagnosis helps separate CDN settings, proxies, and client faults. For XHTTP, a CDN that closes downloads after idle periods shows up as long requests dropping; the XHTTP discussion notes that Cloudflare cuts HTTP responses that carry no data for 100 seconds.[6]
Before changes, keep private copies of server.json, the Nginx site file, the firewall rules, and the client file. Rolling back the client means restoring its previous profile and confirming a fresh request. On the server, disable the new Nginx site and the xray-cdn service, reload Nginx, and confirm with sudo ss -lntp that nothing listens on ports 10001 or 10002.
To retire the entry, remove the proxied DNS record or switch it off the CDN, delete the UUID from any remaining configuration, and revoke or delete the origin certificate if nothing else uses it. Remove the CDN-only firewall rules last, after you know which other sites on the server still depend on port 443. Upgrades follow the same pattern: stage a new version directory, run -test on both files, and repeat the verification table before switching the service.
packet-up for the conservative CDN path, not both on one client.Not automatically. The CDN terminates the client's TLS and opens its own connection to your origin, so it can see the proxied HTTP stream that carries your VLESS traffic.
Flexible sends traffic from the edge to your origin without TLS. Full (strict) validates the origin certificate, which protects the second leg and stops impersonation of your origin.
Yes, with separate secret paths and separate Nginx locations, as in the example. Give each client one transport so you know which path a failure belongs to.
The project's guidance names packet-up as the most compatible mode behind CDNs and reverse proxies, and it keeps the origin as plain HTTP/1.1 without gRPC settings.
Only if the origin firewall admits the CDN's address ranges alone. Without that rule, anyone who learns the VPS address can reach Nginx directly and bypass the edge.
No. A CDN-fronted entry can still be throttled, filtered, or limited by the CDN itself, and edge restarts close open connections. Keep a separate recovery path.
Check the CDN's published ranges on a schedule and after any notice from the provider. An outdated allow list can silently reject legitimate edge connections to your origin.
Disclaimer: This article is for general informational purposes only and does not constitute legal, technical, or other professional advice. We make no guarantees regarding the accuracy, completeness, or timeliness of the content.
Sources:
Sources checked 14 October 2026.
Related Articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





