Transport — Xray-core
Xray's transport choices live under streamSettings. The pattern is:
"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
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
header | json.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. |
acceptProxyProtocol | bool | false | true | false | Inbound — 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
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
host | string | (server address) | <hostname> | HTTP Host header sent on the WebSocket upgrade. Client priority: host > serverName > address. |
path | string | / | /<path>[?ed=<bytes>] | WebSocket path. The ?ed=N query parameter enables 0-RTT early-data carrying up to N bytes. |
headers | map[string]string | {} | {<header>: <value>} | Extra HTTP headers. Putting host here is deprecated; use the top-level host field instead. |
acceptProxyProtocol | bool | false | true | false | Inbound — accept PROXY-protocol on the underlying TCP connection. |
heartbeatPeriod | uint32 | 0 (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).
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
host | string | (server address) | <hostname> | HTTP Host header. |
path | string | / | /<path>[?ed=<bytes>] | Path served. ?ed=N enables early-data. |
headers | map[string]string | {} | {<header>: <value>} | Extra request headers. host is not allowed here — set it via the top-level host field. |
acceptProxyProtocol | bool | false | true | false | Accept PROXY-protocol on the underlying TCP connection. |
Source: infra/conf/transport_method.go:650-655 · pinned at v26.9.9 (52a412d)
network: grpc
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
authority | string | (server address) | <authority> | HTTP/2 :authority pseudo-header sent in the gRPC request. |
serviceName | string | (required) | <service path> | gRPC service name. The full request path is /{serviceName}/Tun or /{serviceName}/TunMulti depending on multiMode. |
multiMode | bool | false | true | false | Multiplex many logical streams over one gRPC bidi stream. Reduces latency for short connections. |
idle_timeout | int32 | 60 | <seconds> | Idle timeout for the gRPC connection. The peer is pinged after this interval; non-response closes the connection. |
health_check_timeout | int32 | 20 | <seconds> | Timeout for the ping response. |
permit_without_stream | bool | false | true | false | Allow gRPC keepalive pings even with no active stream. |
initial_windows_size | int32 | 0 (lib default) | <bytes> | Initial HTTP/2 stream-level window size. Useful for high-BDP links. |
user_agent | string | (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
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
mtu | *uint32 | 1350 | >= 21 | Maximum mKCP segment size, in bytes. Must be at least 21. |
tti | *uint32 | 50 | 10-1000 ms | Transmission-time interval in milliseconds (10–1000). Lower values mean snappier retransmits at the cost of CPU. |
uplinkCapacity | *uint32 | 5 | <MB/s> | Estimated uplink bandwidth in MB/s. mKCP uses this to size its send window. |
downlinkCapacity | *uint32 | 20 | <MB/s> | Estimated downlink bandwidth in MB/s. |
cwndMultiplier | *uint32 | (default) | >= 1 | Congestion-window multiplier — scales how aggressively mKCP grows its window. Must be at least 1. |
maxSendingWindow | *uint32 | (default) | >= mtu | Upper bound on the sending window. Must be greater than or equal to mtu, otherwise the config is rejected at build. |
header | json.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.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
host | string | (server address) | <hostname> | HTTP Host header. |
path | string | / | /<path> | Path prefix. |
mode | string | auto | auto | packet-up | stream-up | stream-one | SplitHTTP 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. |
headers | map[string]string | {} | {<header>: <value>} | Extra headers added to every request. |
xPaddingBytes | Int32Range | (unset) | {from, to} | Range of random padding bytes added to each request. |
xmux | XmuxConfig | (unset) | XmuxConfig | X-Mux connection-pool tuning. |
downloadSettings | *StreamConfig | (unset) | StreamConfig | Alternate 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:
{
"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:
{
"streamSettings": {
"network": "grpc",
"security": "reality",
"grpcSettings": {
"serviceName": "GunService",
"multiMode": true,
"idle_timeout": 60
},
"realitySettings": { /* ... */ }
}
}XHTTP + REALITY with asymmetric download:
{
"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. Usesplithttp(XHTTP) instead — it covers HTTP/2 and HTTP/3. - WebSocket
pathaccepts 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
hostinsideheaderswith an explicit error (infra/conf/transport_method.go:672-674). WebSocket merely emits a deprecation warning for the same pattern. - mKCP has no boolean
congestionand noreadBufferSize/writeBufferSizefields; its congestion behaviour is tuned withcwndMultiplier(window-growth multiplier, ≥ 1) andmaxSendingWindow(window ceiling, ≥mtu). - mKCP's
headerandseedfields are parsed but ignored. Their equivalent is amkcp-legacyentry instreamSettings.finalmask.udp:settings.headerpicks the header disguise (dns,dtls,srtp,utp,wechat,wireguard; fordns,valueis the domain), and withoutheader,settings.valueis 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 masksheader-custom,fragment,sudoku,xmc; UDP masksheader-custom,mkcp-legacy,noise,salamander,sudoku,xdns,xicmp,realm,udphop(infra/conf/transport_finalmask.go:72-89) — including a smalltransformexpression DSL for byte-level rewriting.udphopis the client-side port-hopping mask covered on the Hysteria2 page. It is an advanced area; see the upstreamfinalmaskpackage for the full per-type schema. - XHTTP's
downloadSettingsenables 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_timeoutandhealth_check_timeoutuse 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
*-optsblocks (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)
