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 | Salamander obfuscation block. 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. |
brutal_debug | bool | false | true | false | Log Brutal congestion-control internals. |
Source: option/hysteria2.go:13-23 · pinned at v1.13.11 (553cfa1)
The struct embeds ListenOptions and InboundTLSOptionsContainer. 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 | Obfuscation type. Only `salamander` is currently defined. |
password | string | (required) | <string> | Obfuscation password (separate from the user password). |
Source: option/hysteria2.go:25-28 · pinned at v1.13.11 (553cfa1)
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:30-33 · pinned at v1.13.11 (553cfa1)
masquerade
The masquerade field is polymorphic (option/hysteria2.go:44-95):
- 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:35-40 · pinned at v1.13.11 (553cfa1)
type: "file"
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
directory | string | (required) | <dir path> | Local directory served at the masquerade endpoint. |
Source: option/hysteria2.go:97-99 · pinned at v1.13.11 (553cfa1)
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:101-104 · pinned at v1.13.11 (553cfa1)
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:106-110 · pinned at v1.13.11 (553cfa1)
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. |
up_mbps | int | 0 | <Mbps> | Estimated uplink bandwidth in Mbps. |
down_mbps | int | 0 | <Mbps> | Estimated downlink bandwidth in Mbps. |
obfs | *Hysteria2Obfs | (disabled) | Hysteria2Obfs | Salamander obfuscation; must match the server. |
password | string | (required) | <string> | User auth password. |
network | NetworkList | (tcp+udp) | tcp | udp | | Restrict to TCP-only or UDP-only. |
brutal_debug | bool | false | true | false | Log Brutal CC internals on the client side. |
Source: option/hysteria2.go:112-124 · pinned at v1.13.11 (553cfa1)
Embeds DialerOptions, ServerOptions (server, server_port), and OutboundTLSOptionsContainer (tls — required).
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.typeonly has one defined value today (salamander). Configs that omitobfsuse the unobfuscated path.masqueradeaccepts both the polymorphic typed object and a plain string URL — both are unmarshaled to the same internal representation (option/hysteria2.go:59-78).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. 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:13-124 · v1.13.11 (553cfa1)
