← 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.

This material is about engineering your own infrastructure and is educational in nature. Complying with the laws of your own jurisdiction is your responsibility.

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.