Hands-On: Building a VLESS + WebSocket + TLS + CDN Proxy
Chapters
In the previous chapter, we covered the theory behind TLS-based tunneling and CDN obfuscation. Now it's time to put that knowledge into practice. In this chapter, you'll deploy a production-grade proxy stack — VLESS + WebSocket + TLS + Cloudflare CDN — on a real VPS that already hosts a web project.
By the end, you'll have:
- Proxy traffic disguised as ordinary HTTPS WebSocket connections
- Your VPS's real IP hidden behind Cloudflare's CDN
- A convincing fallback — anyone visiting your domain sees a real, working website
The entire setup coexists with an existing web application on the same server.
Architecture Overview
From an external observer's perspective, this looks like any other Cloudflare-fronted website — indistinguishable from billions of legitimate sites.
Prerequisites
| Requirement | Details |
|---|---|
| VPS | A server already running a web project |
| Domain | Configured with Cloudflare DNS (we'll use proxy.example.com) |
| Nginx | Already installed and running |
| Cloudflare account | Domain already hosted on Cloudflare |
This tutorial assumes you already have a Cloudflare Origin Certificate and a working Nginx setup on your VPS. If that's not the case, set those up first — Cloudflare's docs walk you through it in minutes.
Install Xray
After installation completes, you'll have:
- Binary:
/usr/local/bin/xray - Config file:
/usr/local/etc/xray/config.json - Systemd service:
xray.service
Verify the installation:
Generate a UUID
Each client authenticates with a unique UUID:
You'll get something like: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Save this — you'll need it for both the server and client configs.
Generate a Random WebSocket Path
The WebSocket path acts as an additional secret — only those who know it can reach the proxy:
This produces something like: b428ee8316
Your full WebSocket path will be /b428ee8316.
Never use guessable paths like /ws, /ray, /v2ray, or /proxy. The GFW and security scanners actively probe these common paths. A random hex string is your safest bet.
Configure Xray
Edit the config file:
Paste the following (replace placeholders with your UUID and path):
Here's what each key parameter does:
| Parameter | Purpose |
|---|---|
port: 10001 | Local port Xray listens on (Nginx forwards traffic here) |
listen: 127.0.0.1 | Binds to localhost only — never exposed externally |
protocol: vless | Uses the VLESS protocol |
decryption: none | VLESS doesn't add its own encryption (TLS is handled by Nginx) |
network: ws | Transport layer uses WebSocket |
path | The WebSocket path — clients must match this exactly |
The listen: "127.0.0.1" setting is critical. It ensures Xray only accepts connections from the local machine (i.e., from Nginx). Even if someone discovers the port number, they cannot connect to Xray directly from the outside.
Start Xray
Confirm the output shows Active: active (running). If it fails to start, validate your JSON config:
Configure Nginx
This is the most critical piece — Nginx simultaneously handles proxy traffic and legitimate website traffic through path-based routing.
Create the Site Configuration
Write the following:
Key Directives Explained
WebSocket forwarding essentials:
| Directive | Why it's needed |
|---|---|
proxy_http_version 1.1 | WebSocket requires HTTP/1.1 |
Upgrade + Connection headers | Tells the backend to upgrade the connection to WebSocket |
proxy_read_timeout 300s | WebSocket connections are long-lived; the default 60s timeout would kill them |
Fallback camouflage essentials:
| Directive | Why it's needed |
|---|---|
proxy_pass http://127.0.0.1:3000 | Forwards non-proxy requests to your real website |
proxy_redirect (both lines) | Fixes Next.js redirects that include the internal port number |
You need both the http:// and https:// proxy_redirect rules. Next.js's i18n middleware can generate redirects like https://domain:3000/en — an http-only rule won't match Location headers starting with https. This is a subtle bug that's easy to miss.
Enable the Configuration
Configure Cloudflare
DNS Record
Add the following DNS record in the Cloudflare dashboard:
| Type | Name | Content | Proxy |
|---|---|---|---|
| A | proxy | YOUR_VPS_IP | Proxied (orange cloud) |
The orange cloud (Proxied) is essential — it routes traffic through Cloudflare's CDN, hiding your real VPS IP. A grey cloud (DNS Only) exposes your IP directly.
SSL/TLS Mode
Navigate to SSL/TLS → Overview and set the mode to Full or Full (Strict):
| Mode | Meaning | Recommendation |
|---|---|---|
| Flexible | Cloudflare → VPS uses HTTP | Not secure |
| Full | Cloudflare → VPS uses HTTPS (no cert validation) | Acceptable |
| Full (Strict) | Cloudflare → VPS uses HTTPS (validates cert) | Best |
Confirm WebSocket Support
Go to Network → WebSockets → ensure it's On (enabled by default on Cloudflare's free plan).
Client Configuration
Clash (YAML)
v2rayN Settings
| Parameter | Value |
|---|---|
| Address | proxy.example.com |
| Port | 443 |
| UUID | Your UUID |
| Encryption | none |
| Transport | ws |
| Path | /YOUR_RANDOM_PATH |
| TLS | Enabled |
| SNI | proxy.example.com |
| Skip cert verify | No |
Set skip-cert-verify to false (disabled). When traffic goes through Cloudflare CDN, the certificate is a legitimate Cloudflare-issued cert — there's no reason to skip verification. Setting it to true can actually cause some clients to use an abnormal TLS handshake flow, resulting in connection timeouts.
Deployment Verification
Check that Xray is Running
Test the WebSocket Endpoint Locally
You should get 101 Switching Protocols — this confirms Xray's WebSocket listener is working.
Test the Full Chain (from outside)
Browser Verification
Open https://proxy.example.com in a browser. You should see your real website — identical to visiting it directly. Anyone (including censorship systems) who accesses this domain sees a completely normal, operational website.
Troubleshooting
Here are three real issues encountered during deployment:
Issue 1 - CDN Connection Timeout After Enabling Proxied Mode
Symptom: Client connects fine with grey cloud (DNS Only), but times out after switching to orange cloud (Proxied).
Root cause: With CDN enabled, the TLS certificate changes from your Origin Certificate to a legitimate Cloudflare-issued certificate. If the client has skip-cert-verify: true, some clients use a non-standard TLS handshake path that Cloudflare rejects.
Fix: Set skip-cert-verify: false in your client config. The Cloudflare certificate is valid — let TLS verification proceed normally.
Issue 2 - Browser Redirects to Port 3000
Symptom: Visiting https://proxy.example.com redirects to https://proxy.example.com:3000/en.
Root cause: Next.js's i18n middleware generates redirects using the internal port number it sees from the reverse proxy (https://domain:3000/en).
Fix: Add both proxy_redirect rules in the Nginx fallback location:
Issue 3 - Browser Shows Insecure Connection Warning
Symptom: Browser displays "Your connection is not private" or similar certificate error.
Root cause: The Cloudflare DNS record is set to grey cloud (DNS Only). The browser connects directly to the VPS, but the Origin Certificate installed there is only trusted by Cloudflare — not by browsers.
Fix: Switch the DNS record to orange cloud (Proxied) so traffic flows through Cloudflare, which presents its own trusted certificate to browsers.
Security Hardening
Verify Xray Only Listens on Localhost
If you see 0.0.0.0:10001, the listen field in your Xray config is missing the 127.0.0.1 binding — fix it immediately.
Firewall Rules
Only expose necessary ports:
Do not open port 10001 — Xray only needs local access.
Keep Xray Updated
Comparison with the Reality Scheme
With this deployment complete, you now have two viable proxy approaches:
| Dimension | VLESS + WS + TLS + CDN (this chapter) | VLESS + Reality (chapter 2) |
|---|---|---|
| IP hiding | Yes — IP is behind CDN | No — IP is exposed |
| Resilience to IP bans | High — switch CDN edge node if blocked | Low — need a new VPS |
| Performance | Good (slight WS + CDN overhead) | Excellent (direct TCP) |
| Active probing resistance | Strong (fallback to real website) | Strongest (mimics major site TLS) |
| Deployment complexity | Moderate (needs domain + CDN + cert) | Low (no domain needed) |
| Recommended role | Primary (IP safety) | Backup (when performance matters) |
Best practice: use the WS + CDN setup (this chapter) as your daily driver to protect your VPS IP. Switch to the Reality scheme only when you need maximum performance — for large downloads, real-time gaming, or latency-sensitive work.
Summary
In this chapter, you deployed a production-grade proxy system with four layers:
- Xray — runs the VLESS protocol, listens on a local WebSocket port
- Nginx — routes by path: proxy traffic goes to Xray, everything else falls back to the real website
- Cloudflare CDN — hides the VPS's real IP, provides a free trusted TLS certificate
- Client — standard VLESS + WS + TLS configuration
The core strength of this architecture is triple camouflage:
- Traffic camouflage: looks like ordinary HTTPS WebSocket traffic
- Domain camouflage: served through CDN, just like any normal website
- Fallback camouflage: visiting the domain directly shows a real, actively maintained website
No external observer can distinguish proxy traffic from legitimate website traffic on this domain.
In the next chapter, we'll upgrade the transport layer from WebSocket to XHTTP — a newer protocol that offers better performance characteristics and even stronger resistance to traffic analysis, while maintaining the same CDN compatibility.