--- title: "Stealth Trojan VPN Behind Cloudflare Guide" date: 2025-09-09T11:05:00+02:00 draft: false tags: ["VPN", "Trojan", "Cloudflare", "Xray", "3x-ui", "Bypass", "Privacy", "Debugging"] categories: ["Guides"] --- ## Introduction So you want a VPN that **doesn't scream "I am a VPN"** to every censor and firewall out there? Welcome to the world of **Trojan over WebSocket + TLS behind Cloudflare**. This guide not only shows you how to set it up but also sprinkles in some **debugging magic** so you can figure out why things break (and they *will* break, trust me). We’ll anonymise domains and secrets, so substitute with your own: - VPN domain: `web.example.com` - Panel domain: `panel.example.com` - Secret WS path: `/stealth-path_abcd1234` - Password: `` --- ## Architecture at a Glance Think of it as a disguise party: - **Trojan** = the shy guest (your VPN protocol) - **Nginx** = the bouncer checking IDs (reverse proxy) - **Cloudflare** = the doorman who makes sure nobody sees who's inside (CDN & proxy) - **Your fake website** = the mask (camouflage page) Traffic flow: ``` Client → Cloudflare (443) → Nginx (443) → Trojan (localhost:54321) ``` --- ## Step 1: Panel Setup (`panel.example.com`) - Bind the 3x-ui panel to localhost (e.g., `127.0.0.1:46309`). - Choose a funky **web base path** like `/panel-bananas_42/`. - Proxy it through Nginx with HTTPS + Basic Auth. - Test it at `https://panel.example.com/panel-bananas_42/`. **Pro tip:** If you see a blank page → your base path is mismatched or Nginx is eating it. Check logs! --- ## Step 2: Trojan Inbound In 3x-ui, create a Trojan inbound: - Local port: `54321` - Transport: WebSocket - Path: `/stealth-path_abcd1234` - Security: none - Password: `` --- ## Step 3: Nginx for VPN Domain (`web.example.com`) Your Nginx is the gatekeeper. Sample config: ```nginx server { listen 443 ssl http2; server_name web.example.com; ssl_certificate /etc/letsencrypt/live/web.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/web.example.com/privkey.pem; # Fake website at root location / { root /var/www/html; index index.html; } # Real VPN under secret WS path location /stealth-path_abcd1234 { proxy_pass http://127.0.0.1:54321; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; } } ``` --- ## Step 4: Firewall Rules Lock things down! Only Cloudflare should reach you: ```bash ufw allow 22/tcp ufw allow 80/tcp ufw allow 443/tcp ufw deny 54321/tcp ``` --- ## Step 5: Client Config Trojan URI: ``` trojan://@web.example.com:443?type=ws&security=tls&host=web.example.com&path=%2Fstealth-path_abcd1234&sni=web.example.com#MyStealthVPN ``` iOS clients: Shadowrocket, Stash, FoXray Android clients: v2rayNG, Clash Meta, NekoBox --- ## Debugging Time (a.k.a. “Why the heck doesn’t it work?!”) ### Symptom: **520 Unknown Error (Cloudflare)** - Likely cause: Nginx couldn’t talk to Trojan. - Check Nginx error log: ```bash tail -n 50 /var/log/nginx/error.log ``` - If you see `upstream sent no valid HTTP/1.0 header` → you’re mixing HTTPS/HTTP between Nginx and Trojan. --- ### Symptom: **526 Invalid SSL certificate** - Cloudflare → Nginx cert mismatch. - Run: ```bash openssl s_client -connect web.example.com:443 -servername web.example.com -showcerts ``` - Make sure CN = `web.example.com`. If not, fix your cert. --- ### Symptom: **Blank page on panel** - Check if the base path matches exactly (case-sensitive, slash-sensitive). - Watch for sneaky trailing spaces! - Test locally: ```bash curl -s -D- http://127.0.0.1:46309/panel-bananas_42/ ``` --- ### Symptom: **Client won’t connect** - Run a WebSocket test: ```bash curl -i -k -H "Connection: Upgrade" -H "Upgrade: websocket" https://web.example.com/stealth-path_abcd1234 ``` Expect `101 Switching Protocols`. If not, check Nginx config. --- ### Symptom: **Censor still blocks you** - Did you expose another service (like SSH, Matrix, or Minecraft) on the same IP? → They’ll find your origin IP. Use a second VPS or lock those ports down. - Did you use an obvious path like `/ws`? → Use a random-looking one like `/cdn-assets-329df/`. --- ## Pro Tips & Fun Tricks - Serve a fake blog or portfolio at root so your domain looks legit. - Rotate WebSocket paths occasionally. - Use Cloudflare **Page Rules** to disable Rocket Loader & Minify for your VPN domain. - Make friends with your Nginx error.log — it will roast you but it tells the truth. --- ## Conclusion By putting Trojan behind Cloudflare, you’ve given your VPN a shiny new disguise: - Looks like normal HTTPS. - Hides your origin IP. - Forces censors into a tough choice: block Cloudflare (and half the web) or let you pass. Congrats — you’ve built a VPN with both **style** and **stealth**. 🥷 --- {{< sigdl >}}