mysite/content/posts/vpn.md

4.9 KiB
Raw Permalink Blame History

title date draft tags categories
Stealth Trojan VPN Behind Cloudflare Guide 2025-09-09T11:05:00+02:00 false
VPN
Trojan
Cloudflare
Xray
3x-ui
Bypass
Privacy
Debugging
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).

Well 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: <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: <PASSWORD>

Step 3: Nginx for VPN Domain (web.example.com)

Your Nginx is the gatekeeper. Sample config:

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:

ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw deny 54321/tcp

Step 5: Client Config

Trojan URI:

trojan://<PASSWORD>@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 doesnt it work?!”)

Symptom: 520 Unknown Error (Cloudflare)

  • Likely cause: Nginx couldnt talk to Trojan.
  • Check Nginx error log:
    tail -n 50 /var/log/nginx/error.log
    
  • If you see upstream sent no valid HTTP/1.0 header → youre mixing HTTPS/HTTP between Nginx and Trojan.

Symptom: 526 Invalid SSL certificate

  • Cloudflare → Nginx cert mismatch.
  • Run:
    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:
    curl -s -D- http://127.0.0.1:46309/panel-bananas_42/
    

Symptom: Client wont connect

  • Run a WebSocket test:
    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?
    → Theyll 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, youve 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 — youve built a VPN with both style and stealth. 🥷


{{< sigdl >}}