NEW Check if your VPN is honest — a free subscription audit in 60 seconds
← Back to library
Protocols Deprecated

Trojan: the config (and why it's going obsolete)

⚠ This protocol is outdated and rarely used. Kept for reference — not recommended as your primary setup.

I keep Trojan as a backup of a different "kind" — in case someone starts fingerprinting the VLESS pattern. It disguises itself as an ordinary HTTPS site and, when poked, serves a real page. Below are two working configs and an honest take on why I don't make it my primary channel. Fill in your data in the constructor above.

Where Trojan belongs

Let me set expectations right away so they don't run too high. Trojan is a backup, not the primary channel under Russian DPI. Its point is that it's of a different protocol "kind": if a client's specific VLESS pattern starts getting fingerprinted, Trojan may not fall under the same wave — from the outside it looks like an ordinary HTTPS site. When poked with a browser, it serves a real page via fallback, and only proxies clients with the correct password.

But bare Trojan+TLS exposes your domain's SNI exactly the same way honest VLESS+TLS does — and gets cut off by domain lists. So if you're going to use Trojan under DPI, do it paired with Reality. Both variants are below.

Variant A — Trojan + TLS (your own domain, with fallback to a site)

Honest TLS on your own certificate, with a fallback to a real page. First, the cert:

bash
apt -y install certbot
certbot certonly --standalone -d your-domain.com -m admin@your-domain.com --agree-tos --non-interactive

The full config profile:

config.json
{
  "log": { "loglevel": "none" },
  "inbounds": [
    {
      "tag": "trojan-tls",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "trojan",
      "settings": {
        "clients": [{ "password": "STRONG_PASSWORD" }],
        "fallbacks": [{ "dest": "8080" }]
      },
      "sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] },
      "streamSettings": {
        "network": "raw",
        "security": "tls",
        "tlsSettings": {
          "serverName": "your-domain.com",
          "alpn": ["h2", "http/1.1"],
          "certificates": [
            {
              "certificateFile": "/etc/letsencrypt/live/your-domain.com/fullchain.pem",
              "keyFile": "/etc/letsencrypt/live/your-domain.com/privkey.pem"
            }
          ]
        }
      }
    }
  ],
  "outbounds": [
    { "tag": "DIRECT", "protocol": "freedom" },
    { "tag": "BLOCK",  "protocol": "blackhole" }
  ],
  "routing": { "rules": [
    { "ip": ["geoip:private"], "outboundTag": "BLOCK" },
    { "domain": ["geosite:category-ads-all"], "outboundTag": "BLOCK" },
    { "protocol": ["bittorrent"], "outboundTag": "BLOCK" }
  ]}
}

fallbacks → dest 8080 — "stray" traffic without the correct password goes to a real site/nginx on :8080; poke it with a browser and you'll see a normal page. In the panel you can leave clients empty (the panel will populate it); the password is shown here for clarity.

Variant B — Trojan + Reality (no domain/cert of your own)

The stealth is more robust here — Reality masquerades as someone else's donor, and no cert of your own is needed. Keys:

bash
xray x25519          # privateKey → into the config
openssl rand -hex 8  # shortId
config.json
{
  "log": { "loglevel": "none" },
  "inbounds": [
    {
      "tag": "trojan-reality",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "trojan",
      "settings": { "clients": [{ "password": "STRONG_PASSWORD" }] },
      "sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] },
      "streamSettings": {
        "network": "raw",
        "security": "reality",
        "realitySettings": {
          "show": false,
          "target": "your-donor.de:443",
          "serverNames": ["your-donor.de"],
          "privateKey": "PRIVATE_KEY_X25519",
          "shortIds": ["REALITY_SHORT_ID"]
        }
      }
    }
  ],
  "outbounds": [
    { "tag": "DIRECT", "protocol": "freedom" },
    { "tag": "BLOCK",  "protocol": "blackhole" }
  ],
  "routing": { "rules": [
    { "ip": ["geoip:private"], "outboundTag": "BLOCK" },
    { "domain": ["geosite:category-ads-all"], "outboundTag": "BLOCK" },
    { "protocol": ["bittorrent"], "outboundTag": "BLOCK" }
  ]}
}

Host settings in the panel

Host
Password     : STRONG_PASSWORD
Address      : your-domain.com   (or node IP/domain)
Port         : 443
SNI / Host   : your-domain.com   (Variant B — your-donor.de)
publicKey    : REALITY_PUBLIC_KEY  (Variant B only)
shortId      : REALITY_SHORT_ID    (Variant B only)
fingerprint  : firefox

Why it's going obsolete

The honest part. Trojan is a fading breed, and here's why I don't build a service on it:

  • Bare Trojan+TLS exposes SNI — the same weakness as honest VLESS+TLS, cut off by domain lists. Without Reality, under harsh Russian DPI it stands out.
  • The Reality wrapping saves it, but then it's simpler to just take VLESS+Reality — the same stealth, a more developed ecosystem, active support. Trojan is only justified as a "different kind" to diversify protocols on a node.
  • The password should be long, with no spaces (openssl rand -hex 16), identical on the server and the host. It's Trojan's only secret — a weak password kills the whole scheme.

Bottom line: keep Trojan as a backup protocol of a different "kind" in case of targeted VLESS fingerprinting, but build your primary channel on VLESS+Reality. The Reality mechanics, which do the heavy lifting here too, I covered in the Reality theory piece.

Next guide Shadowsocks: why it's no longer an option → Article unclear or something off? Message me and I will help or fix it. @notrealvpn →
This material is educational and covers network-infrastructure engineering. You are responsible for complying with the laws of your jurisdiction.