Skip to content

Hysteria 2

Hysteria 2 is a QUIC-based proxy protocol. Because it runs over UDP, it often performs well on lossy or long-distance links where TCP-based protocols slow down. MeowX implements the Hysteria 2 client natively, including Salamander obfuscation and port hopping. Use it when your provider offers Hysteria 2 nodes and your network does not block or heavily throttle UDP.

Nodes use the mihomo / Clash Meta config format (type: hysteria2).

proxies:
- name: hy2-node
type: hysteria2
server: example.com
port: 443
password: your-password
proxies:
- name: hy2-full
type: hysteria2
server: 192.0.2.1
# port is optional when "ports" is set
ports: "443,8443-8450"
hop-interval: 30
password: your-password
sni: example.com
obfs: salamander
obfs-password: your-obfs-password
Field Type Required Default Description
name string Yes — Node name. Must be unique and must not be a built-in name such as DIRECT or REJECT.
type string Yes — hysteria2.
server string Yes — Server domain name or IP address.
port integer Yes, unless ports is set — Server UDP port. Ignored when ports is set.
password string Yes — Authentication password sent to the server.
sni string No value of servername, then server TLS server name sent in the QUIC handshake.
servername string No — Accepted as an alias of sni; sni wins if both are set.
obfs string No none Obfuscation type. Only salamander (case-insensitive) is supported. Empty means no obfuscation.
obfs-password string Required with obfs — Salamander password. Must be non-empty and must match the server.
ports string No — Port hopping range, e.g. "443,8443-8450". See Port hopping.
hop-interval integer or string No 30 Seconds between hops: a number (30, 30s) or a random range (10-30). Only used with ports.
skip-cert-verify boolean No false Skip verification of the server certificate. Needed for self-signed certificates. See Certificates.
dialer-proxy string No — Not supported for Hysteria 2 — see Chained proxies.

These common mihomo fields are accepted in the file but have no effect:

  • up, down — bandwidth hints for the Brutal congestion controller. Brutal is not implemented; MeowX uses the QUIC library’s standard congestion control.
  • alpn — the ALPN is always h3.
  • fingerprint, ca, ca-str — certificate pinning and custom CAs are not supported; see Certificates.
  • udp — UDP is always available on Hysteria 2 nodes, whatever this field says.
  • udp-mtu, cwnd, fast-open and other tuning options.

When ports is set, MeowX connects to a random port from the list and, every hop-interval seconds, moves the connection to a new random port. Each hop opens a new local UDP socket; the old socket keeps receiving for one more round so in-flight replies are not lost. The QUIC connection itself — and every proxied connection riding on it — continues uninterrupted.

Rules for ports:

  • Comma-separated (,) or slash-separated (/) items.
  • Each item is a single port (443) or a range (8443-8450). A reversed range (8450-8443) is accepted.
  • Port 0 is invalid. Overlapping ranges are merged, so every port has the same chance of being picked.
  • When ports is present, port is ignored and may be omitted.

Rules for hop-interval:

  • A positive number of seconds, with or without a trailing s.
  • A range such as 10-30 picks a random interval in that range before each hop.
  • Values shorter than 5 seconds are raised to 5 seconds.
  • Omitted: 30 seconds.

Port hopping requires a server that accepts traffic on all of those ports — usually a single Hysteria 2 listener with a firewall rule (for example a DNAT rule) forwarding the whole range to it.

With obfs: salamander, every UDP packet is scrambled with a key derived from obfs-password, so the traffic no longer looks like QUIC. The server must have Salamander enabled with the same password.

  • UDP is always supported and is full-cone: one session can talk to many destinations, and replies from any of them come back.
  • All TCP connections through a node share a single QUIC connection. Each UDP session uses its own QUIC connection.

dialer-proxy cannot be used on a Hysteria 2 node: QUIC runs over UDP and cannot be carried through a TCP proxy. A Hysteria 2 node with dialer-proxy is skipped (a warning appears in the logs). A Hysteria 2 node can still be the target of another node’s dialer-proxy — see Chained proxies.

MeowX verifies the Hysteria 2 server certificate against the system’s trusted root certificates, matching it against the SNI (sni, or server when no SNI is set — an IP address is matched against the certificate’s IP entries). Missing intermediate certificates are fetched automatically. Verification happens right after the QUIC handshake and before your password is sent, so a server with the wrong certificate never receives it.

If the server uses a self-signed certificate, or a certificate that doesn’t match the name you connect to, the connection fails with “server certificate not trusted”. Set skip-cert-verify: true on that node to connect anyway:

skip-cert-verify: true

If sni is not set, MeowX sends the value of server as the SNI — even when it is an IP address. The official Hysteria 2 server checks the SNI by default (sniGuard: dns-san): when its certificate contains a domain name and the SNI does not match it, the server aborts the handshake. The official client behaves the same way, so this is a server-side configuration matter.

If a node whose server is an IP address cannot connect, set sni to the domain in the server’s certificate:

server: 192.0.2.1
sni: example.com
Problem Result
password missing or empty The whole configuration fails to load.
Neither port nor a non-empty ports The whole configuration fails to load.
obfs is anything other than salamander Node is dropped with a warning.
obfs: salamander without obfs-password Node is dropped with a warning.
ports cannot be parsed (e.g. contains 0, letters, or empty items) Node is dropped with a warning.
ports is set and hop-interval is not a positive number or a-b range Node is dropped with a warning.
dialer-proxy is set Node is skipped with a warning in the logs.

Dropped nodes do not break the rest of the subscription. Warnings appear in the app’s logs.