NGINX with QUIC+HTTP/3

We’ve recently added LL-HLS to stream and one of the soft requirements for the feature is the use of an HTTP/2 or HTTP/3 reverse proxy. There are many different reverse proxies that you can use (although Apache is not recommended). NGINX is one reverse proxy that works well with aiohttp (the web server used by HA). I’ve been using a docker image built from the nginx-quic branch of NGINX and it has been working quite well for me, so I’m sharing my config here. There are other posts on creating SSL/TLS certificates and setting up NGINX with HA, so please reference those threads if you have any questions on those topics. Feel free to comment or edit this post if you have any suggestions or improvements.

Building the docker image:

Use the Dockerfile at the bottom of Our Roadmap for QUIC and HTTP/3 Support in NGINX - NGINX , but remove the line:
hg clone http://hg.nginx.org/njs && \
and the argument:
--add-module=../njs/nginx
as njs currently causes build problems with boringssl. Place this Dockerfile in a directory named nginx-quic.
To build the container image, run docker build nginx-quic (sudo permissions may be required). After it’s done, you should get the message “Successfully built <image id>”.

Creating the container:

The docker image is now built, and you can use it like you would the regular nginx docker image. You can create a container from the image using the following docker-compose.yaml:

version: "3.9"
services:
  nginx:
    container_name: nginx
    image: <image id>
    volumes:
      - <path to directory outside container>/ssl:/ssl
      - <path to directory outside container>/conf.d:/etc/nginx/conf.d
    network_mode: host

Above, <path to directory outside container> is a folder somewhere for this container, and beneath this folder are two subfolders named ssl and conf.d. The ssl directory will contain your key, certificates, and dh parameters, while conf.d will contain your nginx.conf file.

Creating the nginx.conf file:

The following is my nginx.conf:

map $http_upgrade $connection_upgrade {
    default     upgrade;
    ''          close;
}

upstream homeassistant {
    server 127.0.0.1:8123;
    keepalive 32;
}

server {

    listen [::]:443 http3 reuseport ipv6only=off;
    listen [::]:443 ssl http2 ipv6only=off;

    server_name <your host name>;
    ssl_certificate /ssl/fullchain.pem;
    ssl_certificate_key /ssl/privkey.pem;
    ssl_dhparam /ssl/dhparams.pem;

    # Similar to the intermediate configuration from https://ssl-config.mozilla.org/
    # Ideally we would also remove TLSv1.2 and the ssl_ciphers line, but it seems like the HA iOS app has problems with TLSv1.3
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:1m; # 1m should be more than enough
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    #ssl_stapling doesn't work with boringssl
    #ssl_stapling on;
    #ssl_stapling_verify on;
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;

    # Using proxy buffers may free up HA sooner on slower connections.
    # Make sure that you don't see any "an upstream response is buffered to a temporary file" messages in your logs
    # If you do, increase the buffer size
    proxy_buffers 1024 4k;

    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;
    proxy_intercept_errors off;
    proxy_http_version 1.1;

    proxy_set_header                X-Forwarded-For         $proxy_add_x_forwarded_for;
    proxy_set_header                Host                    $http_host;
    proxy_set_header                X-Real-IP               $remote_addr;
    proxy_set_header                X-Forwarded-Proto       $scheme;
    proxy_set_header                Connection              "";

    location / {
        add_header                  Alt-Svc 'h3=":443"; ma=86400';
        add_header                  Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;
        proxy_pass                  http://homeassistant;
    }

    location /api/websocket {
        proxy_set_header            Upgrade                 $http_upgrade;
        proxy_set_header            Connection              $connection_upgrade;
        proxy_pass                  http://homeassistant/api/websocket;
    }

}

Port forwarding:

Since QUIC uses UDP, you’ll have to remember to configure your router to forward UDP in addition to TCP on your listening port (port 443 in the nginx.conf above).

Hopefully some of you can give this a try and see if you notice any speedup in Lovelace. If you use stream you can try pairing this setup with LL-HLS.