Skip to content

Transport — Xray-core ​

Xray's transport choices live under streamSettings. The pattern is:

json
"streamSettings": {
  "network": "<transport>",
  "security": "<tls|reality|none>",
  "<transport>Settings": { ... }
}

network is one of tcp, ws, httpupgrade, grpc, mkcp, splithttp (also called XHTTP). The matching <transport>Settings block is read. method is accepted as an alias for network — when both are set, method wins.

network: tcp ​

FieldTypeDefaultAllowed valuesDescription
headerjson.RawMessage{type: "none"}{type: "none"} | {type: "http", request: {...}, response: {...}}Optional header obfuscation. The default none is plain TCP. The HTTP header form sends a fake HTTP request/response to mimic plain web traffic.
acceptProxyProtocolboolfalsetrue | falseInbound — accept a PROXY-protocol v1/v2 header prepended by an upstream load balancer.

Source: infra/conf/transport_method.go:231-234 · pinned at v26.9.9 (52a412d)

tcpSettings is the default — most setups use plain TCP with TLS as the security layer.

network: ws — WebSocket ​

FieldTypeDefaultAllowed valuesDescription
hoststring(server address)<hostname>HTTP Host header sent on the WebSocket upgrade. Client priority: host > serverName > address.
pathstring//<path>[?ed=<bytes>]WebSocket path. The ?ed=N query parameter enables 0-RTT early-data carrying up to N bytes.
headersmap[string]string{}{<header>: <value>}Extra HTTP headers. Putting host here is deprecated; use the top-level host field instead.
acceptProxyProtocolboolfalsetrue | falseInbound — accept PROXY-protocol on the underlying TCP connection.
heartbeatPerioduint320 (disabled)<seconds>Send WebSocket ping frames at this interval to keep NAT mappings alive. 0 disables.

Source: infra/conf/transport_method.go:608-614 · pinned at v26.9.9 (52a412d)

network: httpupgrade ​

A lighter cousin of WebSocket — uses the HTTP-upgrade handshake but not the full WebSocket framing afterwards. The post-handshake stream is plain bytes, saving a few percent CPU at the cost of WS framing benefits (e.g. close codes).

FieldTypeDefaultAllowed valuesDescription
hoststring(server address)<hostname>HTTP Host header.
pathstring//<path>[?ed=<bytes>]Path served. ?ed=N enables early-data.
headersmap[string]string{}{<header>: <value>}Extra request headers. host is not allowed here — set it via the top-level host field.
acceptProxyProtocolboolfalsetrue | falseAccept PROXY-protocol on the underlying TCP connection.

Source: infra/conf/transport_method.go:650-655 · pinned at v26.9.9 (52a412d)

network: grpc ​

FieldTypeDefaultAllowed valuesDescription
authoritystring(server address)<authority>HTTP/2 :authority pseudo-header sent in the gRPC request.
serviceNamestring(required)<service path>gRPC service name. The full request path is /{serviceName}/Tun or /{serviceName}/TunMulti depending on multiMode.
multiModeboolfalsetrue | falseMultiplex many logical streams over one gRPC bidi stream. Reduces latency for short connections.
idle_timeoutint3260<seconds>Idle timeout for the gRPC connection. The peer is pinged after this interval; non-response closes the connection.
health_check_timeoutint3220<seconds>Timeout for the ping response.
permit_without_streamboolfalsetrue | falseAllow gRPC keepalive pings even with no active stream.
initial_windows_sizeint320 (lib default)<bytes>Initial HTTP/2 stream-level window size. Useful for high-BDP links.
user_agentstring(lib default)<UA string>User-Agent sent on the underlying HTTP/2 connection.

Source: infra/conf/transport_method.go:573-582 · pinned at v26.9.9 (52a412d)

network: mkcp ​

FieldTypeDefaultAllowed valuesDescription
mtu*uint321350>= 21Maximum mKCP segment size, in bytes. Must be at least 21.
tti*uint325010-1000 msTransmission-time interval in milliseconds (10–1000). Lower values mean snappier retransmits at the cost of CPU.
uplinkCapacity*uint325<MB/s>Estimated uplink bandwidth in MB/s. mKCP uses this to size its send window.
downlinkCapacity*uint3220<MB/s>Estimated downlink bandwidth in MB/s.
cwndMultiplier*uint32(default)>= 1Congestion-window multiplier — scales how aggressively mKCP grows its window. Must be at least 1.
maxSendingWindow*uint32(default)>= mtuUpper bound on the sending window. Must be greater than or equal to mtu, otherwise the config is rejected at build.
headerjson.RawMessage(ignored)(use finalmask mkcp-legacy)Parsed but ignored — use a mkcp-legacy entry in finalmask.udp instead (see Notes).
seed*string(ignored)(use finalmask mkcp-legacy)Parsed but ignored, like header. A seed goes into the value of a mkcp-legacy finalmask entry.

Source: infra/conf/transport_method.go:522-532 · pinned at v26.9.9 (52a412d)

network: splithttp (XHTTP) ​

XHTTP is Xray's modern HTTP-2/3-aware transport. It has 30 fields — this page documents the most-used ones; the rest are covered on the dedicated SplitHTTP page.

FieldTypeDefaultAllowed valuesDescription
hoststring(server address)<hostname>HTTP Host header.
pathstring//<path>Path prefix.
modestringautoauto | packet-up | stream-up | stream-oneSplitHTTP framing mode. auto negotiates with the server. packet-up sends each app-layer write as a separate POST; stream-up uses a single long-lived POST; stream-one keeps even the response open on the same TCP connection.
headersmap[string]string{}{<header>: <value>}Extra headers added to every request.
xPaddingBytesInt32Range(unset){from, to}Range of random padding bytes added to each request.
xmuxXmuxConfig(unset)XmuxConfigX-Mux connection-pool tuning.
downloadSettings*StreamConfig(unset)StreamConfigAlternate stream-settings for the download half (asymmetric setups, e.g. upload via SplitHTTP and download over plain TLS).

Source: infra/conf/transport_method.go:256-287 · pinned at v26.9.9 (52a412d)

The remaining fields cover advanced traffic-shaping (xPaddingObfsMode, xPaddingKey, sessionIDKey, sessionIDPlacement, seqPlacement, uplinkDataKey, …) and the X-Mux connection pool (maxConcurrency, maxConnections, cMaxReuseTimes, …).

Examples ​

WebSocket + TLS:

json
{
  "streamSettings": {
    "network": "ws",
    "security": "tls",
    "wsSettings": {
      "path": "/vl?ed=2048",
      "host": "example.com",
      "headers": { "User-Agent": "Mozilla/5.0..." }
    },
    "tlsSettings": { "serverName": "example.com" }
  }
}

gRPC + REALITY:

json
{
  "streamSettings": {
    "network": "grpc",
    "security": "reality",
    "grpcSettings": {
      "serviceName": "GunService",
      "multiMode": true,
      "idle_timeout": 60
    },
    "realitySettings": { /* ... */ }
  }
}

XHTTP + REALITY with asymmetric download:

json
{
  "streamSettings": {
    "network": "splithttp",
    "security": "reality",
    "splithttpSettings": {
      "mode": "auto",
      "path": "/xhttp",
      "host": "www.cloudflare.com",
      "xmux": {
        "maxConcurrency": { "from": 4, "to": 8 },
        "hMaxReusableSecs": { "from": 300, "to": 360 }
      },
      "downloadSettings": {
        "network": "splithttp",
        "security": "tls",
        "splithttpSettings": { "mode": "stream-one" }
      }
    },
    "realitySettings": { /* ... */ }
  }
}

Notes ​

  • Xray-core has no network: "http" (HTTP/2) transport. Use splithttp (XHTTP) instead — it covers HTTP/2 and HTTP/3.
  • WebSocket path accepts a ?ed=<bytes> query parameter that encodes the maximum early-data size. The parameter is stripped from the on-wire path and applied as a transport setting at config build.
  • HttpUpgrade rejects host inside headers with an explicit error (infra/conf/transport_method.go:672-674). WebSocket merely emits a deprecation warning for the same pattern.
  • mKCP has no boolean congestion and no readBufferSize / writeBufferSize fields; its congestion behaviour is tuned with cwndMultiplier (window-growth multiplier, ≥ 1) and maxSendingWindow (window ceiling, ≥ mtu).
  • mKCP's header and seed fields are parsed but ignored. Their equivalent is a mkcp-legacy entry in streamSettings.finalmask.udp: settings.header picks the header disguise (dns, dtls, srtp, utp, wechat, wireguard; for dns, value is the domain), and without header, settings.value is the seed — empty for plain mKCP obfuscation, non-empty for AES-128-GCM keyed by it (infra/conf/transport_finalmask.go:598-631).
  • finalmask is Xray's packet-masking subsystem (transport/internet/finalmask/*). It provides pluggable building blocks selected by type — TCP masks header-custom, fragment, sudoku, xmc; UDP masks header-custom, mkcp-legacy, noise, salamander, sudoku, xdns, xicmp, realm, udphop (infra/conf/transport_finalmask.go:72-89) — including a small transform expression DSL for byte-level rewriting. udphop is the client-side port-hopping mask covered on the Hysteria2 page. It is an advanced area; see the upstream finalmask package for the full per-type schema.
  • XHTTP's downloadSettings enables asymmetric transport — the client uploads via one transport and downloads via another. Use to evade DPI that fingerprints the request/response symmetry of HTTP.
  • gRPC's idle_timeout and health_check_timeout use the snake_case spelling from the protobuf, not camelCase like the rest of Xray's surface.

Cross-core notes ​

  • sing-box uses a polymorphic transport: { type: "ws|http|grpc|httpupgrade|quic", ... } block embedded on every TLS-capable inbound/outbound. See Transport — sing-box.
  • mihomo distributes transport options across per-protocol *-opts blocks (ws-opts, h2-opts, grpc-opts, xhttp-opts) on each proxy entry. See Transport — mihomo.

Source: infra/conf/transport_method.go:231-655 · v26.9.9 (52a412d)

Core Tutorial by Argsment