Skip to content

Hysteria2 — mihomo

mihomo speaks Hysteria2 on both sides. The outbound carries explicit port-hopping fields and a generous set of QUIC-go flow-control knobs. The inbound's user table is a flat map — one username/password pair per entry.

Outbound

Entry under proxies: with type: hysteria2. Embeds BasicOption (common outbound fields: interface-name, routing-mark, …).

FieldTypeDefaultAllowed valuesDescription
namestring(required)<string>Unique proxy name.
serverstring(required)<host>Upstream server hostname or IP.
portint(unset)<port>Single server port. Omit when `ports` is set.
portsstring(unset)<port-list>Port-hopping list. Accepts a comma-separated mix of single ports and ranges, e.g. `"20000,20002-20100"`. Mutually beneficial with `hop-interval`.
hop-intervalstring30<seconds string> | <duration>Time between port-hops. Plain numeric string (`"30"`) is treated as seconds; Go-style duration (`"30s"`) is also accepted. Minimum is 5 (`adapter/outbound/hysteria2.go:25`).
upstring(unset)<bandwidth>Uplink bandwidth. String with unit, e.g. `"100 Mbps"`, `"500 kbps"`, `"1 Gbps"`.
downstring(unset)<bandwidth>Downlink bandwidth, same format.
passwordstring(unset)<string>User auth password. Optional only if the server allows unauthenticated peers.
obfsstring(disabled)salamanderObfuscation type. Currently only `salamander` is meaningful.
obfs-passwordstring(unset)<string>Obfuscation password, separate from the user password.
obfs-min-packet-sizeint0<bytes>Minimum random padding size for Salamander obfuscation. Only meaningful with `obfs: salamander`.
obfs-max-packet-sizeint0<bytes>Maximum random padding size for Salamander obfuscation.
snistring(server)<SNI>TLS Server Name Indication. Defaults to `server`.
ech-optsECHOptions(disabled)ECHOptionsEncrypted Client Hello configuration.
skip-cert-verifyboolfalsetrue | falseDisable TLS verification (test only).
fingerprintstring(unset)<SHA256 hex>Pin the server's TLS certificate fingerprint.
certificatestring(unset)<PEM file path>Client certificate (mTLS).
private-keystring(unset)<key file path>Private key for `certificate`.
alpn[]string[h3]h3ALPN list. Hysteria2 typically uses `h3`.
cwndint0<int>Initial QUIC congestion window override.
bbr-profilestring(unset)default | aggressiveTuning profile for the bundled Brutal/BBR variant.
udp-mtuint1200<bytes>MTU used for fragmenting UDP payloads inside the QUIC datagram.
realm-optsHysteria2RealmOption(disabled)Hysteria2RealmOptionConnect through a Hysteria2 *realm* — a multi-tenant server reached via a control endpoint. See the realm-opts table below.
initial-stream-receive-windowuint640 (quic-go default)<bytes>Initial flow-control window per stream.
max-stream-receive-windowuint640 (quic-go default)<bytes>Maximum flow-control window per stream.
initial-connection-receive-windowuint640 (quic-go default)<bytes>Initial flow-control window for the whole connection.
max-connection-receive-windowuint640 (quic-go default)<bytes>Maximum flow-control window for the whole connection.

Source: adapter/outbound/hysteria2.go:39-71 · pinned at v1.19.27 (5184081)

Inbound

Entry under listeners: with type: hysteria2. Embeds BaseOption (listen, port).

FieldTypeDefaultAllowed valuesDescription
usersmap[string]string{}{<username>: <password>}User table — map of username to password. Empty allows unauthenticated peers.
obfsstring(disabled)salamanderSalamander obfuscation type.
obfs-passwordstring(unset)<string>Obfuscation password.
obfs-min-packet-sizeint0<bytes>Minimum random padding size for Salamander obfuscation. Only meaningful with `obfs: salamander`.
obfs-max-packet-sizeint0<bytes>Maximum random padding size for Salamander obfuscation.
certificatestring(required)<PEM file path>TLS server certificate. Required for inbound.
private-keystring(required)<key file path>TLS private key.
client-auth-typestring(none)no-client-cert | request-client-cert | require-any-client-cert | verify-client-cert-if-given | require-and-verify-client-certMutual-TLS client-auth mode.
client-auth-certstring(unset)<PEM file path>CA bundle accepted as client roots.
ech-keystring(unset)<ECH config>Encrypted Client Hello material.
max-idle-timeint0<seconds>Idle timeout on the listener side. 0 keeps the QUIC default.
alpn[]string[h3]h3ALPN list offered during the TLS handshake.
upstring(unset)<bandwidth>Uplink bandwidth (string with unit).
downstring(unset)<bandwidth>Downlink bandwidth.
ignore-client-bandwidthboolfalsetrue | falseDiscard the client's bandwidth advertisement and impose the server's settings.
masqueradestring(unset)<URL>Masquerade target — a string URL (`file:///path` or `https://upstream/`).
cwndint0<int>Initial QUIC congestion window.
bbr-profilestring(unset)default | aggressiveBrutal/BBR tuning profile.
udp-mtuint1200<bytes>UDP datagram fragmentation MTU.
mux-optionMuxOption(disabled)MuxOptionsing-style multiplex settings.
realm-optsHysteria2RealmOption(disabled)Hysteria2RealmOptionConnect through a Hysteria2 *realm* — a multi-tenant server reached via a control endpoint. See the realm-opts table below.
initial-stream-receive-windowuint640 (quic-go default)<bytes>Initial per-stream flow window.
max-stream-receive-windowuint640 (quic-go default)<bytes>Maximum per-stream flow window.
initial-connection-receive-windowuint640 (quic-go default)<bytes>Initial connection flow window.
max-connection-receive-windowuint640 (quic-go default)<bytes>Maximum connection flow window.

Source: listener/inbound/hysteria2.go:12-42 · pinned at v1.19.27 (5184081)

realm-opts

Both the outbound and inbound realm-opts blocks share the same shape. On an outbound, they point a proxy at a Hysteria2 realm — a tenant hosted on a multi-realm server — by way of a control endpoint that hands back the actual connection details. Realm mode is a newer, advanced feature; leave it disabled unless you are connecting to a realm server.

FieldTypeDefaultAllowed valuesDescription
enableboolfalsetrue | falseEnable realm mode for this proxy.
server-urlstring(unset)<URL>URL of the realm control server used to obtain realm connection details.
tokenstring(unset)<string>Auth token presented to the realm control server.
realm-idstring(unset)<string>Identifier of the realm to join.
stun-servers[]string(unset)<host:port list>STUN servers used for realm NAT traversal.
snistring(unset)<SNI>TLS SNI for the control-server connection.
skip-cert-verifyboolfalsetrue | falseSkip TLS verification toward the control server (testing only).
fingerprintstring(unset)<SHA256 hex>Pin the control server's TLS certificate fingerprint.
certificatestring(unset)<PEM file path>Client certificate (mTLS) toward the control server.
private-keystring(unset)<key file path>Private key for `certificate`.
alpn[]string(unset)<string list>ALPN for the control-server connection.

Source: adapter/outbound/hysteria2.go:73-87 · pinned at v1.19.27 (5184081)

Examples

Outbound — single port, salamander obfs, 100/300 Mbps:

yaml
proxies:
  - name: hy2-plain
    type: hysteria2
    server: example.com
    port: 443
    password: <password>
    obfs: salamander
    obfs-password: <obfs>
    sni: example.com
    up: 100 Mbps
    down: 300 Mbps
    alpn: [h3]

Outbound — port-hopping across 20000-20100:

yaml
proxies:
  - name: hy2-hop
    type: hysteria2
    server: example.com
    ports: 20000-20100
    hop-interval: 30s
    password: <password>
    obfs: salamander
    obfs-password: <obfs>
    sni: example.com
    up: 200 Mbps
    down: 1 Gbps
    alpn: [h3]

Outbound connecting through a realm via a control server:

yaml
proxies:
  - name: hy2-realm
    type: hysteria2
    server: example.com
    port: 443
    password: <password>
    sni: example.com
    alpn: [h3]
    realm-opts:
      enable: true
      server-url: https://realm.example.com/control
      token: <realm-token>
      realm-id: team-a
      stun-servers:
        - stun.example.com:3478

Inbound with two users and an HTTP-file masquerade:

yaml
listeners:
  - name: hy2-in
    type: hysteria2
    listen: 0.0.0.0
    port: 443
    users:
      alice: <alice-password>
      bob: <bob-password>
    obfs: salamander
    obfs-password: <obfs>
    certificate: /etc/mihomo/server.crt
    private-key: /etc/mihomo/server.key
    up: 500 Mbps
    down: 1 Gbps
    masquerade: file:///var/www
    alpn: [h3]

Notes

  • ports accepts a mix of single ports and ranges separated by commas, e.g. "20000,20002-20100,30000". port and ports are mutually exclusive — pick one. When ports is set, mihomo dials a fresh randomly-selected member every hop-interval.
  • hop-interval minimum is 5 seconds (adapter/outbound/hysteria2.go:25) — values below 5 are clamped up.
  • The bandwidth values accept unit suffixes (b, kbps, Mbps, Gbps, Tbps) with a space between the number and the unit — "100 Mbps". The strings are parsed by mihomo's tools.StringToBps helper.
  • obfs-min-packet-size and obfs-max-packet-size only apply with obfs: salamander. They set the random-padding bounds for the Salamander obfuscator; with any other (or no) obfs they are ignored.
  • mihomo's inbound users is a YAML mapping, not an object list. The key is the username and the value is the password, e.g. users: { alice: pw1, bob: pw2 }. To allow anonymous clients, leave the map empty.
  • The four *-receive-window fields are direct knobs for the bundled quic-go stack. Most users should leave them at 0 (default). Increase the connection-level windows to improve throughput over high-BDP links.

Cross-core notes

  • Xray-core splits the config across settings (version/clients) and streamSettings.hysteriaSettings (auth/bandwidth/masquerade). See Hysteria2 — Xray-core.
  • sing-box keeps the config in a single block but uses plain integer Mbps for bandwidth (no unit suffix) and offers a polymorphic masquerade value (string URL or typed object). See Hysteria2 — sing-box.
  • mihomo can also host realms — see Hysteria2 realm listener.

Source: adapter/outbound/hysteria2.go:39-87 · v1.19.27 (5184081)

Core Tutorial by Argsment