---
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: `<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:

```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://<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 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 >}}
