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 (now also known as XHTTP). The matching <transport>Settings block is read.
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_internet.go:108-111 · pinned at v26.6.1 (94ffd50)
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_internet.go:133-139 · pinned at v26.6.1 (94ffd50)
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_internet.go:175-180 · pinned at v26.6.1 (94ffd50)
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/grpc.go:8-17 · pinned at v26.6.1 (94ffd50)
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. Replaces the old boolean `congestion` switch. |
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 | (removed) | (use finalmask) | Removed. Setting either `header` or `seed` fails at build with a redirect to the new finalmask header transports. |
seed | *string | (removed) | (use finalmask) | Removed. Same as `header`. |
Source: infra/conf/transport_internet.go:53-63 · pinned at v26.6.1 (94ffd50)
network: splithttp (XHTTP)
XHTTP is Xray's modern HTTP-2/3-aware transport. It has 28 fields — this page documents the most-used ones; the rest are covered on the SplitHTTP page (Phase 5).
| 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_internet.go:211-240 · pinned at v26.6.1 (94ffd50)
The remaining fields cover advanced traffic-shaping (xPaddingObfsMode, xPaddingKey, 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
network: "http"(HTTP/2 transport) was removed from Xray-core. 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_internet.go:206-208). WebSocket merely emits a deprecation warning for the same pattern. - mKCP's congestion model changed: the boolean
congestionand thereadBufferSize/writeBufferSizefields are removed, replaced bycwndMultiplier(window-growth multiplier, ≥ 1) andmaxSendingWindow(window ceiling, ≥mtu). - mKCP's
headerandseedfields are removed. Setting either fails at config build with a redirect to the new finalmask header transports (or the dedicatedmkcp-original/mkcp-aes128gcmtypes). - finalmask is Xray's reworked UDP packet-masking subsystem (
transport/internet/finalmask/*). It replaces the old mKCP header-obfuscation with pluggable building blocks selected by type —header-custom,mkcp-original,mkcp-aes128gcm,mkcp-legacy,realm,salamander,noise,xdns,xicmp,sudoku,fragment— including a smalltransformexpression DSL for byte-level rewriting. 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_internet.go:53-240 · v26.6.1 (94ffd50)
