Hysteria2 — sing-box
sing-box's Hysteria2 implementation is the cleanest of the three cores: a single flat block on each side, polymorphic masquerade, and explicit port-hopping fields on the outbound.
Inbound
type: "hysteria2" inbound:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
up_mbps | int | 0 | <Mbps> | Estimated uplink bandwidth in Mbps. The server uses this as a hint for congestion control. |
down_mbps | int | 0 | <Mbps> | Estimated downlink bandwidth in Mbps. |
obfs | *Hysteria2Obfs | (disabled) | Hysteria2Obfs | Obfuscation block (salamander or gecko). When set, both sides must match. |
users | []Hysteria2User | [] | [Hysteria2User] | Accepted users. |
ignore_client_bandwidth | bool | false | true | false | Discard the client's bandwidth advertisement and use the server's bandwidth settings unilaterally. |
masquerade | *Hysteria2Masquerade | (disabled) | Hysteria2Masquerade | HTTP-response masquerade for unauthenticated traffic. Accepts a string URL or a typed object. |
bbr_profile | string | standard | conservative | standard | aggressive | BBR congestion-control profile, used whenever BBR is selected. |
brutal_debug | bool | false | true | false | Log Brutal congestion-control internals. |
realm | *Hysteria2InboundRealm | (disabled) | Hysteria2InboundRealm | Register this server with a Hysteria Realm rendezvous service for NAT traversal: it discovers its public addresses via STUN, registers them under realm_id, and accepts clients by UDP hole-punching — no publicly reachable listen address needed. |
Source: option/hysteria2.go:17-30 · pinned at v1.14.2 (af6e64c)
The struct embeds ListenOptions, InboundTLSOptionsContainer and QUICOptions (see QUIC fields below). A TLS configuration is required — Hysteria2 runs on QUIC and there is no plaintext mode.
obfs
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
type | string | (required) | salamander | gecko | Obfuscation type. gecko also takes min_packet_size / max_packet_size. |
password | string | (required) | <string> | Obfuscation password (separate from the user password). |
Source: option/hysteria2.go:64-68 · pinned at v1.14.2 (af6e64c)
With type: "gecko" the same object also takes:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
min_packet_size | int | 512 | <bytes> | Minimum on-wire packet size in bytes. Gecko only. |
max_packet_size | int | 1200 | <bytes> | Maximum on-wire packet size in bytes. Gecko only. |
Source: option/hysteria2.go:59-62 · pinned at v1.14.2 (af6e64c)
users[]
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
name | string | (unset) | <string> | Display name used in stats and logs. |
password | string | (required) | <string> | User authentication password. |
Source: option/hysteria2.go:116-119 · pinned at v1.14.2 (af6e64c)
masquerade
The masquerade field is polymorphic (option/hysteria2.go:121-181):
- A plain string URL. Schemes:
file:///var/www— equivalent to{ "type": "file", "directory": "/var/www" }.https://upstream.example.com— equivalent to{ "type": "proxy", "url": "..." }.
- An object with a
typefield selecting one of three shapes:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
type | string | (unset) | file | proxy | string | Selects which sub-block is active. |
Source: option/hysteria2.go:121-126 · pinned at v1.14.2 (af6e64c)
type: "file"
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
directory | string | (required) | <dir path> | Local directory served at the masquerade endpoint. |
Source: option/hysteria2.go:195-197 · pinned at v1.14.2 (af6e64c)
type: "proxy"
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
url | string | (required) | <URL> | Upstream URL the masquerade endpoint reverse-proxies to. |
rewrite_host | bool | false | true | false | Rewrite the Host header to match the upstream URL. |
Source: option/hysteria2.go:199-202 · pinned at v1.14.2 (af6e64c)
type: "string"
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
status_code | int | 200 | <int> | HTTP status code returned. |
headers | badoption.HTTPHeader | {} | {<header>: <value>} | Extra response headers. |
content | string | (required) | <text> | Response body. |
Source: option/hysteria2.go:204-208 · pinned at v1.14.2 (af6e64c)
Outbound
type: "hysteria2" outbound:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
server_ports | badoption.Listable[string] | [] | <range> | Port-hopping list. Each entry is a port (e.g. "20001") or a hyphenated range (e.g. "20001-20100"). |
hop_interval | badoption.Duration | 30s | <duration> | How often to switch to a new port. Accepts Go-style durations. |
hop_interval_max | badoption.Duration | (unset) | <duration> | Upper bound for randomized port hopping: each hop waits a random time between hop_interval and this value. |
up_mbps | int | 0 | <Mbps> | Estimated uplink bandwidth in Mbps. |
down_mbps | int | 0 | <Mbps> | Estimated downlink bandwidth in Mbps. |
obfs | *Hysteria2Obfs | (disabled) | Hysteria2Obfs | Obfuscation (salamander or gecko); must match the server. |
password | string | (required) | <string> | User auth password. |
network | NetworkList | (tcp+udp) | tcp | udp | | Restrict to TCP-only or UDP-only. |
bbr_profile | string | standard | conservative | standard | aggressive | BBR congestion-control profile, used whenever BBR is selected. |
brutal_debug | bool | false | true | false | Log Brutal CC internals on the client side. |
disable_chrome_parrot | bool | false | true | false | Turn off Chrome QUIC-handshake parroting, which is on by default. Parroting applies Chrome's QUIC parameters (idle_timeout fixed at 30s; Chrome's max_concurrent_streams and initial_packet_size; receive windows start at Chrome's initial values) and fails against servers using Ed25519 certificates. |
realm | *Hysteria2Realm | (disabled) | Hysteria2Realm | Reach the server through a Hysteria Realm: query the realm for the addresses registered under realm_id, hole-punch, then run the normal QUIC handshake. Conflicts with server, server_port and server_ports. |
Source: option/hysteria2.go:210-227 · pinned at v1.14.2 (af6e64c)
Embeds DialerOptions, ServerOptions (server, server_port), OutboundTLSOptionsContainer (tls — required) and QUICOptions.
Realm (NAT traversal)
A server behind NAT sets realm on its inbound and registers with a Hysteria Realm service; clients set realm on the outbound (instead of server) to find it. Both sides share this shape:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
server_url | string | (required) | <URL> | Realm rendezvous service URL. |
token | string | (unset) | <string> | Bearer token; must match one of the realm's users[].token. |
realm_id | string | (required) | <id> | Slot identifier. The server registers under it and clients must use the same value; 1–64 characters matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$. |
stun_servers | badoption.Listable[string] | (required) | <host[:port]> | … | STUN servers used to discover public addresses. On the outbound, domain names are resolved with domain_resolver from the dial fields. |
ip_version | int | (both) | 4 | 6 | Restrict STUN, hole punching and the resulting QUIC path to one IP version. |
port_mapping | *Hysteria2RealmPortMapping | (disabled) | Hysteria2RealmPortMapping | Keep a UDP port mapping on the local gateway via UPnP or NAT-PMP; failures are non-fatal. Requires IPv4. |
http_client | *HTTPClientOptions | (default) | <tag> | HTTPClientOptions | HTTP client used to talk to the realm (an inline object or the tag of an http_clients entry). |
Source: option/hysteria2.go:32-40 · pinned at v1.14.2 (af6e64c)
The inbound form adds:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
stun_domain_resolver | *DomainResolveOptions | (default resolver) | <dns server tag> | DomainResolveOptions | Inbound only: resolver for STUN server domain names (same format as domain_resolver). |
Source: option/hysteria2.go:54-57 · pinned at v1.14.2 (af6e64c)
port_mapping:
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
enabled | bool | false | true | false | Enable port mapping. |
timeout | badoption.Duration | 10s | <duration> | Timeout for gateway discovery and mapping operations. |
lifetime | badoption.Duration | 10m | <duration> | Lease lifetime of the mapping; it is renewed at half of it. |
Source: option/hysteria2.go:48-52 · pinned at v1.14.2 (af6e64c)
QUIC fields
Both sides embed the QUIC parameters shared with Hysteria, TUIC and HTTP/3 clients (QUICOptions, which in turn embeds HTTP2Options). With Chrome parroting enabled (the client default), idle_timeout is fixed at 30 seconds and max_concurrent_streams / initial_packet_size are replaced by Chrome's values.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
initial_packet_size | int | (QUIC default) | <bytes> | Initial QUIC packet size. |
disable_path_mtu_discovery | bool | false | true | false | Disable QUIC path MTU discovery. |
Source: option/http.go:22-26 · pinned at v1.14.2 (af6e64c)
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
idle_timeout | badoption.Duration | (default) | <duration> | Idle connection timeout. |
keep_alive_period | badoption.Duration | (default) | <duration> | Keep-alive period. |
stream_receive_window | *byteformats.MemoryBytes | (default) | <size> | Per-stream flow-control receive window, as a memory size (e.g. 64 MB). |
connection_receive_window | *byteformats.MemoryBytes | (default) | <size> | Per-connection flow-control receive window, as a memory size. |
max_concurrent_streams | int | (default) | <int> | Maximum concurrent streams per connection. |
Source: option/http.go:14-20 · pinned at v1.14.2 (af6e64c)
Examples
Inbound with port hopping not exposed (server side just listens on one port), Salamander obfs, and a file-masquerade:
{
"inbounds": [
{
"type": "hysteria2",
"tag": "hy2-in",
"listen": "::",
"listen_port": 443,
"users": [
{ "name": "alice", "password": "<password>" }
],
"obfs": { "type": "salamander", "password": "<obfs>" },
"tls": {
"enabled": true,
"alpn": ["h3"],
"certificate_path": "/etc/ssl/cert.pem",
"key_path": "/etc/ssl/key.pem"
},
"masquerade": "file:///var/www"
}
]
}Outbound with port hopping (20000-20100, switch every 30 seconds):
{
"outbounds": [
{
"type": "hysteria2",
"tag": "hy2-out",
"server": "example.com",
"server_port": 443,
"server_ports": ["20000-20100"],
"hop_interval": "30s",
"password": "<password>",
"obfs": { "type": "salamander", "password": "<obfs>" },
"up_mbps": 100,
"down_mbps": 300,
"tls": { "enabled": true, "server_name": "example.com" }
}
]
}Notes
- Bandwidth values are plain integer Mbps here — no unit string. Xray and mihomo accept suffixed strings (
"100mbps"), sing-box does not. obfs.typeissalamanderorgecko. Configs that omitobfsuse the unobfuscated path.- The client parrots Chrome's QUIC handshake by default. Chrome does not advertise Ed25519, so a server with an Ed25519 certificate fails the handshake — use an ECDSA or RSA certificate, or set
disable_chrome_parrot: trueon the client. - On the outbound, leaving
up_mbps/down_mbpsunset selects BBR (tunable withbbr_profile) instead of Brutal. masqueradeaccepts both the polymorphic typed object and a plain string URL — both are unmarshaled to the same internal representation (option/hysteria2.go:145-164).ignore_client_bandwidth: trueis the recommended setting for servers whose admin already knows the real bandwidth — it prevents a malicious client from understating its capacity to extract more from the server.
Cross-core notes
- Xray-core supports Hysteria2 but splits config between
settingsandstreamSettings.hysteriaSettings, with bandwidth, congestion control and port hopping understreamSettings.finalmask. See Hysteria2 — Xray-core. - mihomo uses a single-block outbound with
up/downas strings (with unit suffixes like Xray). Port-hopping isports+hop-interval. Users on the inbound are amap[string]string(username → password) rather than an object list. See Hysteria2 — mihomo.
Source: option/hysteria2.go:17-227 · v1.14.2 (af6e64c)
