This year, like many Habr visitors, I read with great interest the articles by the respected @MiraclePtr I learned to apply his ideas and recommendations, getting practical experience with protocols, clients, and graphical panels. For many protocols, there are detailed installation and configuration instructions available to even the most inexperienced users who are just starting to explore the world of Linux.

I finally got around to the protocol briefly described in the article Modern Anti-Censorship Technologies: V2Ray, XRay, XTLS, Hysteria, Cloak, and Everything Else, Hysteria, which has already reached its second version. But I couldn't find a comprehensive Russian-language guide for it, which prompted me to gather all the information in one place once I figured out the main issues of installing and configuring the server and clients to use this protocol.

Аяха Хидери, официальный аниме-талисман Hysteria
Ayaha Hideri, the official anime mascot of Hysteria

Hysteria is a fast, cross-platform protocol based on a modified QUIC protocol with a wide range of modes (SOCKS5, HTTP proxy, TCP/UDP forwarding, Linux TProxy), support for user authentication, traffic statistics and access control, port forwarding, and masquerading as standard HTTP/3 traffic, which makes it difficult to detect and block. Despite the fact that QUIC is almost completely blocked in Russia, Hysteria allows for traffic obfuscation, which, however, can lead to it being blocked along with other unidentified protocols. There are no panels for Hysteria yet, but in practice, it's quite easy to configure the server and clients without them.

The protocol is evolving, works well, and may be useful to you alongside currently popular protocols like Shadowsocks, XTLS-Reality, and others. We don't put all our eggs in one basket, do we?

Installation

Hysteria2 Homepage
Official Hysteria Repository

From now on, I'll assume you have a VPS with Debian/Ubuntu and SSH access to it (as well as root privileges on it). For other Linux distributions, the syntax and commands may differ, but the general meaning of the commands will be similar. I'll admit right away, I'm not a great Linux expert, so comments and advice are welcome.

You will also need a domain, which you can buy for a year for a small amount of money (for example, on spaceship.com), or get for free on dynu.com.

Let's consider two options for installing Hysteria on a server: in a Docker container or directly on the server.

Running in a Docker Container

You can install the Docker engine by following the official guide.

Download the image with the command:

sudo docker pull tobyxdd/hysteria

Create a folder for the configuration:

sudo mkdir -p /etc/hysteria

Create the configuration file server.yaml (configuration parameters are discussed below), in which you specify the path to your domain certificate files (if you are using your own certificates and not the acme.sh script) and the rest of the server configuration parameters. You need to ensure the files are readable.

sudo nano /etc/hysteria/server.yaml

Run the container, specifying the same port as in the configuration file:

sudo docker run -d -p 443:443/udp --name hysteria --restart=always -e TZ=Europe/Moscow -v /etc/hysteria:/etc/hysteria teddysun/hysteria

You can view the container logs with this command:

sudo docker logs hysteria

Installation on the Server

The following commands are for running as a regular user with elevated privileges using the sudo command. As preparation for installation for Linux beginners, here are the recommended commands after deploying the OS image on a VPS:

sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y && sudo apt install curl -y

After this, I usually reboot the VPS to allow for the installation of an updated kernel.

As with most modern VPN/proxy protocols, the developers provide a script for installing/updating or removing Hysteria on a Linux server. For the root user, Hysteria installation/update is performed with the command:

bash <(curl -fsSL https://get.hy2.sh/)

Running this script as a regular user with elevated privileges using the sudo command gives me an error about lack of access for curl to special files /dev/fd/xx. This is solved by simply downloading the script to the user's folder and then running it with elevated privileges using the sudo:

wget https://get.hy2.sh/install_server.sh && chmod 755 install_server.sh && sudo ./install_server.sh

After installation, you need to decide how to specify the domain certificate and private key: you can either provide the path to your existing certificate files or let Hysteria automatically obtain (and renew) the certificate using the built-in acme.sh script by specifying the domain and email in the server configuration file.

Server Configuration

After that, you need to edit the server configuration. A description of all server configuration file parameters is available here. In YAML, space indentation (tab characters are not allowed) is used to denote structure, so it is not recommended to remove or add extra spaces. Some of the confusing errors I encountered during setup and testing of different modes were due to an extra/missing space.

sudo nano /etc/hysteria/config.yaml

The default configuration looks like this:

# listen: :443

acme:
  domains:
    - your.domain.net
  email: your@email.com

auth:
  type: password
  password: o9s+p5+sR5Bi13XWBjjf2ymW

masquerade:
  type: proxy
  proxy:
    url: https://news.ycombinator.com/
    rewriteHost: true

This is the set of necessary parameters for the configuration file. Let's break down these parameters.

# listen: :443

By default, Hysteria listens on port 443 (as the default port for HTTP/3), but if you want to use a different port, you need to uncomment this parameter and specify the port you want.

acme:
  domains:
    - your.domain.net
  email: your@email.com

Here you need to specify your domain and email to obtain a certificate using the acme.sh script. The certificates will be automatically obtained and saved in a subfolder of the used certificate authority at the path /var/lib/hysteria/acme/certificates

The full list of parameters for this section:

acme:
  domains:
    - domain1.com
    - domain2.org
  email: your@email.net
  ca: zerossl 
  listenHost: 0.0.0.0 
  dir: my_acme_dir 
  type: http | tls | dns 
  http:
    altPort: 8888 
  tls:
    altPort: 44333 
  dns:
    name: gomommy 
    config:
      key1: value1
      key2: value2

Additional useful parameters for this section:
ca - the certificate authority to use, valid values are letsencrypt or zerossl , for Russian domains you must specify letsencrypt
dir - the folder for saving certificates obtained via the ACME protocol
type - the type of ACME challenge, I usually use for Cloudflare dns (more details here) specifying the Cloudflare API token with DNS editing permissions for the specified domains:

  dns:
    name: cloudflare
    config:
      cloudflare_api_token: Dxabckw9dB_jYBdi89kgyaS8wRjqqSsd679urScKOBP

Alternatively, you can specify your existing certificate files:

tls:
  cert: your_cert.crt
  key: your_key.key

In this case, you need to ensure that the certificate files are readable by Hysteria; this command is sufficient for that

sudo chmod 644 -Rf путь_к_файлам_сертификата

For example, if the certificate files are in /etc/ssl/private, then this parameter takes the following form:

tls:
  cert: /etc/ssl/private/fullchain.cer
  key: /etc/ssl/private/private.key

Authentication

This section defines authentication. Password, HTTP, and command-based authentication are possible.

When choosing password authentication, you can use a single password or a multi-user configuration:

auth:
  type: password
  password: o9s+p5+sR5Bi13XWBjjf2ymW
auth:
  type: userpass
  userpass:
    user1: pass1
    user2: pass2
    user3: pass3

HTTP authentication is set up like this:

auth:
  type: http
  http:
    url: http://your.backend.com/auth
    insecure: false

When using HTTP authentication, upon a client connection attempt, the server will send a POST request to an internal server with the following JSON:

{
  "addr": "123.123.123.123:44556", 
  "auth": "something_something", 
  "tx": 123456 
}

Your endpoint must respond with a JSON object with the following fields:

{
  "ok": true, 
  "id": "john_doe" 
}

When using command-based authentication, upon a client connection attempt, the server will execute the specified command with the following arguments (the arguments are the same as for HTTP authentication):

/etc/some_command addr auth tx

The command sends the client's unique ID to stdout and returns code 0 if the connection with the client is allowed, or a non-zero code if the client is rejected.

If the command fails to execute, the client will also be rejected.

Masquerading

The “masquerade” section defines the masquerading used. One of the key aspects of censorship resistance in Hysteria is its ability to masquerade as standard HTTP/3 traffic. This means that not only do packets appear as HTTP/3 to intermediate nodes, but the server also responds to HTTP requests like a regular web server.

However, this means your server must actually serve some content to appear genuine to potential censors.

If censorship is not a concern, you can completely omit the masquerade section. In this case, Hysteria will always return "404 Not Found" for all HTTP requests.

Currently, Hysteria provides the following "masquerade" modes:

  • file: Acts as a static file server, serving files from a directory.

  • proxy: Acts as a reverse proxy, forwarding content from another site.

  • string: Acts as a server that always returns a user-defined string.

  type: file
  file:
    dir: /www/masq
  type: proxy
  proxy:
	url: https://news.ycombinator.com/
	rewriteHost: true
  type: string
  string:
    content: hello stupid world
    headers:
      content-type: text/plain
      custom-stuff: ice cream so good
    statusCode: 200 

I usually choose a live website located in my VPS's subnet (the BGP Toolkit helps me with this)

Traffic Obfuscation

By default, the Hysteria protocol mimics HTTP/3. If your network specifically blocks QUIC or HTTP/3 traffic (but not UDP in general), you can use obfuscation to bypass this. Hysteria currently implements an obfuscation method called "Salamander," which transforms packets into random, patternless bytes. This feature requires a password that must be the same on both the client and server sides (Enabling obfuscation will make your server incompatible with standard QUIC connections, and it will no longer function as a valid HTTP/3 server)

To enable traffic obfuscation, add the following section to the configuration

obfs:
  type: salamander
  salamander:
    password: cry_me_a_r1ver

Port Hopping

Users in China sometimes report that their ISPs block or throttle persistent UDP connections. However, these restrictions often apply only to the specific port being used. As a workaround in this situation, you can use port hopping.

Client Configuration

The Hysteria client supports a special multi-port address format:

example.com:1234,5678,9012
example.com:20000-50000
example.com:1234,5000-6000,7044,8000-9000

The number of ports you can specify is unlimited.
The client will randomly select one of the specified ports for the initial connection and periodically switch to another port. The option to control the interval is hopInterval in the transport section:

transport:
  udp:
    hopInterval: 30s

Server Configuration

The Hysteria server does not have built-in support for listening on multiple ports, so you cannot use the format above as the listening address on the server side. The developers recommend using iptables DNAT to redirect ports to the server's listening port.

# IPv4
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 20000:50000 -j DNAT --to-destination :443
# IPv6
ip6tables -t nat -A PREROUTING -i eth0 -p udp --dport 20000:50000 -j DNAT --to-destination :443

In this example, the server listens on port 443, but the client can connect to any port in the 20000-50000 range.

As suggested by the respected @Renaissance, for port hopping with nftables, you can use redirect in the NAT table (assuming hysteria2 is running on the default port 443 and there is no separation for ipv4/ipv6):

udp dport 20000-50000 redirect to 443

Useful Server Configuration Items

You can specify which DNS server to use for resolving domain names in client requests.

resolver:
  type: udp
  tcp:
	addr: 8.8.8.8:53
	timeout: 4s
  udp:
	addr: 8.8.4.4:53
	timeout: 4s
  tls:
	addr: 1.1.1.1:853
	timeout: 10s
	sni: cloudflare-dns.com
	insecure: false
  https:
	addr: 1.1.1.1:443
	timeout: 10s
	sni: cloudflare-dns.com
	insecure: false

ACL rules, often used in conjunction with outbounds, are a powerful feature of the Hysteria server that allows you to customize how client requests are handled. With ACL, you can block specific addresses or use different outbounds for different sites. You can use either a file-based or inline method for specifying processing rules. The available ACL rules are detailed here.

As reminded by the respected @Renaissance, the developers do not recommend using Hysteria to proxy HTTP/3 traffic (when HTTP/2 or lower can be used):

"Currently, all websites and applications use QUIC only as an "upgrade". If the network doesn't support it (e.g., UDP is blocked), it falls back to HTTP/2 or lower (which uses TCP).
If you are using Chrome or Firefox with an HTTP/SOCKS5 proxy on a PC, the browser itself has effectively disabled HTTP/3, as an HTTP proxy cannot support UDP forwarding. As for SOCKS5, although it theoretically supports UDP, it is not implemented in Chrome and Firefox.
If you are using a VPN client like Shadowrocket, SagerNet on your phone (or TUN on a PC), it is recommended to manually disable HTTP/3 in one of the following ways":

  • Chrome: Open the page chrome://flags/, find the parameter Experimental QUIC protocol and switch its value to Disabled

  • Firefox: Open the page about:config, find the parameter network.http.http3.enable and switch its value to false

  • Block UDP port 443 with an ACL rule (inline):
    - reject(all, udp/443)

Currently, Hysteria supports the following types of outbound connections:

  • direct: Direct connection through the local interface.

  • socks5: SOCKS5 proxy.

  • http: HTTP/HTTPS proxy.

Specifically, I use a WARP proxy on the server and route some sites through it, which allows me to use services like OpenAI.com without any issues. I have also disabled access to Russian resources (just in case mischievous hands disable the proxy bypass for them in the client).

outbounds:
  - name: warp_proxy
	type: socks5
	socks5:
  	addr: 127.0.0.1:40000
acl:
  inline:
# WARP proxy
  - warp_proxy(suffix:google.com)
  - warp_proxy(suffix:openai.com)
# Block RU
  - reject(geoip:ru)
# Block Google Ads
  - reject(geosite:google@ads)
# Block UDP port 443
  - reject(all, udp/443)
# Direct all other connections
  - direct(all)

There are many scripts for installing WARP in proxy mode; I used this one, installation is done with the command

cd && bash <(curl -fsSL git.io/warp.sh) proxy

Finalizing the Installation

After editing the configuration file, all that's left is to start the service and add it to the services that run at server startup:

sudo systemctl start hysteria-server.service
sudo systemctl enable hysteria-server.service

You can view the service log with this command:

sudo journalctl -u hysteria-server.service

Client Setup

You can use the official client for your OS, which can be downloaded from Github, or third-party clients, a list of which is available here.

The official client for Windows x64 (https://download.hysteria.network/app/latest/hysteria-windows-amd64.exe) is launched with a file named config in the same directory, with the following content, whose parameters must match the server configuration file:

server: your.domain.net:443
auth: RZhuNfVtgQgQuPInznsiygIq
obfs:
  type: salamander
  salamander:
	password: cry_me_a_r1ver
http:
  listen: 127.0.0.1:8080
socks5:
  listen: 127.0.0.1:1080

In a multi-user configuration, the auth: section looks like this:

auth: user:pass

A description of all client configuration file parameters is available here.

Among third-party clients, you can use NekoBox for Android / Nekoray for Windows or Hiddify for your required OS.

In NekoBox/Nekoray, when adding a configuration, you need to select the Hysteria type, enter the domain name in the Server and SNI fields, specify the remote port on the server, the connection password, and the obfuscation password.

In Hiddify, you just need to paste the configuration copied to the clipboard in this format:

{
  "outbounds":   [
    {
      "type": "hysteria2",
      "tag": "Hysteria 2",
      "server": "IP of your domain as xx.xx.xx.xx",
      "server_port": 443,
      "obfs":       {
        "type": "salamander",
        "password": "cry_me_a_r1ver"
      },
      "password": "o9s+p5+sR5Bi13XWBjjf2ymW",
      "tls":       {
        "enabled": true,
        "server_name": "your.domain.net"
      }
    }
  ]
}