Skip to content

Trojan — Xray-core

Trojan is a TLS-shaped tunnel with a password handshake. In Xray-core the implementation matches the original Trojan spec and does not support the XTLS flow extension — the flow field is still parsed for compatibility but any non-empty value produces a hard error (infra/conf/trojan.go:73, 128).

Outbound

settings for an outbound of "protocol": "trojan":

FieldTypeDefaultAllowed valuesDescription
address*Address(unset)<host>Simplified shape — server hostname or IP.
portuint16(required with address)<port>Server port.
levelbyte0<byte>User level (simplified shape).
emailstring(unset)<string>User identifier reported in stats.
passwordstring(required with address)<string>Shared password for the Trojan handshake.
flowstring(must be empty)Removed feature. Setting any non-empty value rejects the config at startup.
servers[]*TrojanServerTarget(use simplified shape)[TrojanServerTarget]Verbose shape. Must hold exactly one server; multiple servers require multiple outbounds + a balancer.

Source: infra/conf/trojan.go:31-39 · pinned at v26.6.1 (94ffd50)

servers[]

FieldTypeDefaultAllowed valuesDescription
address*Address(required)<host>Server hostname or IP.
portuint16(required)<port>Server port.
levelbyte0<byte>User level.
emailstring(unset)<string>User identifier.
passwordstring(required)<string>Trojan password.
flowstring(must be empty)Removed; non-empty value is rejected.

Source: infra/conf/trojan.go:21-28 · pinned at v26.6.1 (94ffd50)

Simplified vs. servers

The outbound accepts either the simplified top-level shape (address, port, password, ...) or the verbose servers shape with exactly one entry. For multi-server setups use multiple Trojan outbounds plus a routing balancer.

Inbound

settings for an inbound of "protocol": "trojan":

FieldTypeDefaultAllowed valuesDescription
users[]*TrojanUserConfig[TrojanUserConfig] | …Inbound account list; same object shape as `clients`. `users` is the newer name introduced in recent Xray and is accepted alongside `clients`.
clients[]*TrojanUserConfig(required)[TrojanUserConfig]List of accepted users.
fallbacks[]*TrojanInboundFallback[][TrojanInboundFallback]Fallback destinations consulted when the incoming TLS-wrapped traffic does not look like Trojan.

Source: infra/conf/trojan.go:114-118 · pinned at v26.6.1 (94ffd50)

clients[]

FieldTypeDefaultAllowed valuesDescription
passwordstring(required)<string>Trojan password.
levelbyte0<byte>Policy level for this user.
emailstring(unset)<string>Tag in stats/log output.
flowstring(must be empty)Removed; non-empty value is rejected.

Source: infra/conf/trojan.go:106-111 · pinned at v26.6.1 (94ffd50)

fallbacks[]

FieldTypeDefaultAllowed valuesDescription
namestring(unset)<TLS server name>Match incoming TLS SNI; empty matches any.
alpnstring(unset)h2 | http/1.1 | Match the negotiated ALPN; empty matches any.
pathstring(unset)/<path>HTTP path prefix. Must start with `/` if non-empty.
typestring(auto)tcp | unix | serveBackend protocol. Inferred from `dest` when omitted.
destjson.RawMessage(required)<host:port> | <port> | <unix path>Forwarding target. Bare integer = port on localhost.
xveruint6400 | 1 | 2PROXY-protocol version. 0 disables.

Source: infra/conf/trojan.go:96-103 · pinned at v26.6.1 (94ffd50)

The Trojan fallback chain mirrors VLESS: matched in order on the first byte of the TLS-wrapped stream, with (name, alpn, path) as the keys.

Examples

Minimal inbound with one user and TLS:

json
{
  "inbounds": [
    {
      "tag": "trojan-in",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "trojan",
      "settings": {
        "clients": [
          { "password": "<password>", "email": "alice" }
        ]
      },
      "streamSettings": { "network": "tcp", "security": "tls" }
    }
  ]
}

Simplified outbound:

json
{
  "outbounds": [
    {
      "tag": "trojan-out",
      "protocol": "trojan",
      "settings": {
        "address": "example.com",
        "port": 443,
        "password": "<password>",
        "email": "alice"
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "wsSettings": { "path": "/tj" }
      }
    }
  ]
}

Notes

  • flow on Trojan is a removed feature in Xray-core. The struct field still exists for backwards-compatible parsing, but supplying any non-empty value triggers PrintRemovedFeatureError("Flow for Trojan") at startup. The runtime also prints a deprecation banner recommending "VLESS with Flow & Seed" (infra/conf/trojan.go:42, 120).
  • password is required. An outbound with empty password fails to build (infra/conf/trojan.go:69-71).
  • fallbacks work exactly like VLESS fallbacks — same field shape and same auto-inference for type from dest.

Cross-core notes

  • sing-box uses users[] (with name/password) instead of clients[], and replaces Xray's fallback chain with two distinct options: a single fallback target plus fallback_for_alpn keyed by ALPN. See Trojan — sing-box.
  • mihomo has the standard single-proxy-object outbound shape, and also exposes a Shadowsocks-over-Trojan (ss-opts) compatibility layer for trojan-go clients. See Trojan — mihomo.

Source: infra/conf/trojan.go:21-118 · v26.6.1 (94ffd50)

Core Tutorial by Argsment