Hysteria2 — Xray-core
Xray-core supports Hysteria v2 but splits the config across two blocks: the protocol-level settings (version, address/port, users) and the transport-level streamSettings.hysteriaSettings (authentication, bandwidth, port-hopping, masquerade). Both must be filled in for the outbound to be usable.
Outbound — protocol layer
settings for an outbound of "protocol": "hysteria":
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
version | int32 | (required) | 2 | Must be exactly 2. Any other value is rejected at startup (`infra/conf/hysteria.go:19-21`). |
address | *Address | (required) | <host> | Server hostname or IP. |
port | uint16 | (required) | <port> | Server UDP port. |
Source: infra/conf/hysteria.go:13-17 · pinned at v26.6.1 (94ffd50)
Hysteria v1 is unsupported
The version field must equal 2. HysteriaClientConfig.Build (infra/conf/hysteria.go:19-21) returns errors.New("version != 2") for anything else.
Inbound — protocol layer
settings for an inbound of "protocol": "hysteria":
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
version | int32 | (required) | 2 | Must be 2. |
users | []*HysteriaUserConfig | [] | [HysteriaUserConfig] | Accepted users. `users` is the newer name and is accepted alongside `clients`. |
clients | []*HysteriaUserConfig | [] | [HysteriaUserConfig] | Accepted users (legacy name; same shape as `users`). |
Source: infra/conf/hysteria.go:40-44 · pinned at v26.6.1 (94ffd50)
clients[]
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
auth | string | (required) | <string> | Authentication string. |
level | uint32 | 0 | <uint32> | Policy level for this user. |
email | string | (unset) | <string> | Tag in stats/logs. |
Source: infra/conf/hysteria.go:34-38 · pinned at v26.6.1 (94ffd50)
Transport layer — hysteriaSettings
Set under streamSettings.hysteriaSettings. Carries every operational knob the protocol layer omits.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
version | int32 | (required) | 2 | Hysteria protocol version. Must match the `version` in `settings`. |
auth | string | (required on outbound) | <string> | Outbound auth string. On the inbound this field is forwarded to the validator but per-user auth lives in `settings.clients[].auth`. |
congestion | *string | (unset) | bbr | cubic | reno | Server-side hint for congestion-control algorithm. Newer builds move this to QUIC params — a warning is printed if you set it here. |
up | *Bandwidth | (unset) | <bandwidth> | Estimated uplink bandwidth (string with unit: `1Mbps`, `100kbps`, `50mbps`). |
down | *Bandwidth | (unset) | <bandwidth> | Estimated downlink bandwidth. |
udphop | *UdpHop | (unset) | UdpHop | UDP port-hopping configuration for the outbound. |
udpIdleTimeout | int64 | 60 | <2..600 seconds> | Seconds of UDP-stream idleness before the QUIC stream is closed. Must be between 2 and 600 inclusive (`infra/conf/transport_internet.go:544-546`). |
masquerade | Masquerade | (unset) | Masquerade | Inbound-only: HTTP-response masquerade for unauthenticated traffic. |
Source: infra/conf/transport_internet.go:513-524 · pinned at v26.6.1 (94ffd50)
udphop
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
ports | PortList | (required) | <JSON array or string> | Port list to hop across. Accepts JSON arrays of ports/ranges or a string like `"100,200-210,400"`. |
interval | Int32Range | (unset) | Int32Range | Range (`{From, To}`) controlling how often to switch ports, in seconds. |
Source: infra/conf/transport_internet.go:494-497 · pinned at v26.6.1 (94ffd50)
masquerade
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
type | string | (required) | file | proxy | string | Selects which sub-block applies. |
dir | string | (file only) | <dir path> | Directory served when `type: file`. |
url | string | (proxy only) | <URL> | Upstream URL when `type: proxy`. |
rewriteHost | bool | false | true | false | Rewrite the Host header when proxying (`type: proxy`). |
insecure | bool | false | true | false | Skip TLS verification on the upstream when `type: proxy`. |
content | string | (string only) | <text> | Body returned when `type: string`. |
headers | map[string]string | {} | {<header>: <value>} | Extra response headers when `type: string`. |
statusCode | int32 | 200 | <int> | Status code returned when `type: string`. |
Source: infra/conf/transport_internet.go:499-511 · pinned at v26.6.1 (94ffd50)
The type field switches the active sub-block: file uses dir, proxy uses url/rewriteHost/insecure, string uses content/ headers/statusCode.
Bandwidth syntax
up and down are parsed by the helper in infra/conf/transport_internet.go:478-501. Accepted suffixes:
| Suffix | Multiplier |
|---|---|
(empty), b, bps | 1 |
k, kb, kbps | 1024 |
m, mb, mbps | 1 048 576 |
g, gb, gbps | 1 073 741 824 |
t, tb, tbps | 1 099 511 627 776 |
The numeric part is parsed as a float64 and the result divided by 8 (bytes-per-second is what the protobuf carries, but the source unit name is bps).
Examples
Outbound:
{
"outbounds": [
{
"tag": "hy2-out",
"protocol": "hysteria",
"settings": {
"version": 2,
"address": "example.com",
"port": 443
},
"streamSettings": {
"network": "hysteria",
"security": "tls",
"tlsSettings": { "serverName": "example.com" },
"hysteriaSettings": {
"version": 2,
"auth": "<password>",
"up": "100mbps",
"down": "300mbps",
"udpIdleTimeout": 120
}
}
}
]
}Inbound with two users and an HTTP-file masquerade:
{
"inbounds": [
{
"tag": "hy2-in",
"listen": "0.0.0.0",
"port": 443,
"protocol": "hysteria",
"settings": {
"version": 2,
"clients": [
{ "auth": "<alice>", "email": "alice" },
{ "auth": "<bob>", "email": "bob" }
]
},
"streamSettings": {
"network": "hysteria",
"security": "tls",
"tlsSettings": { "certificates": [{ "certificateFile": "/etc/ssl/cert.pem", "keyFile": "/etc/ssl/key.pem" }] },
"hysteriaSettings": {
"version": 2,
"masquerade": {
"type": "file",
"dir": "/var/www"
}
}
}
}
]
}Notes
- A common gotcha: setting
authonly insidesettings(as if it were a username/password field). Xray reads outbound auth fromstreamSettings.hysteriaSettings.auth. Inbound users usesettings.clients[].auth(per-user) and ignore the transport-levelauthfor matching. congestion,up,down, andudphopwill be moved into the newfinalmask/quicParamsblocks in a future release. A warning is printed if these are set on the legacy location (infra/conf/transport_internet.go:540-542).- Hysteria v1 is fully removed from Xray-core. The "v1" name in the source is now just a historical label —
version != 2is a hard failure. udpIdleTimeout < 2or> 600triggers a startup error (infra/conf/transport_internet.go:544-546).
Cross-core notes
- sing-box uses a single, much flatter block — no transport split. Bandwidth is in plain
intMbps (no unit string), and masquerade supports a polymorphic shape (string URL or typed object). See Hysteria2 — sing-box. - mihomo is also single-block, with port hopping driven by
ports(range syntax) plushop-interval. mihomo accepts unit-suffixed strings forup/downlike Xray. See Hysteria2 — mihomo.
Source: infra/conf/hysteria.go:13-44 · v26.6.1 (94ffd50)
