HTTP/3 s Nginx

Nginx je webový server, ktorý je možné použiť v roly cache server, loadbalancer, alebo tiež reverzný proxy. Ja používam Nginx v roly reverzného proxy servera. HTTP znamená „Hypertextový prenosový protokol“ čo je komunikačný protokol, ktorý sa používa na prenos hypertextových dokumentov, ako sú napr. webové stránky, medzi webovým klientom (napr. webový prehliadač) a webovým serverom. Prvá ver. HTTP protokolu HTTP/0.9 bola nasadená v roku 1990 a postupne sa protokol zdokonaľoval cez ver. HTTP/1.0, HTTP/1.1, HTTP/2 a v súčasnej dobe prechádza väčšina webových serverov na HTTP-over-QUIC, resp. HTTP/3.

V minulom blogu som sa pokúšal migrovať z Nginx na Caddy, čo sa mi aj podarilo, ale problém bol v dlhom načitávaní WordPress webov. Čiže s Caddy som po krátkom čase prešiel znova na Nginx. Ak by som ostal pri Caddy, tak HTTP/3 tam funguje po vybalení z krabice a nič extra nie je potrebné nastavovať.

Nginx vydal prvé binárne balíčky s oficiálnou podporou HTTP/3 už 02.08.2023 a to pre 2 veľké distribúcie, Red Hat 9 a Ubuntu 22.04. Nginx teda oficiálne podporuje HTTP/3 s QUIC od verzie 1.25.1. Do vtedy bolo možné tiež experimentálne používať Nginx s podporou QUIC, ale bola potrebná kompilácia zo zdroja. V debiane je to teraz omnoho jednoduchšie a môžeme na to použiť jednoduchý baličkovací nástroj APT.

V dobe písania tohto článku mám Nginx nasadení v linuxovom kontajnery Debian 12. Upozorňujem, že nám nestačí použiť klasicky

udo apt update
sudo apt install curl gnupg2 ca-certificates lsb-release    
sudo apt install nginx -y

Pretože v repozitároch Debian 12 sa nachádza

nginx version: nginx/1.22.1

Táto verzia ešte nepodporuje HTTP/3. Preto musíme systém nastaviť tak, aby neinštaloval Nginx s Debian repozitárov, ale aby použil repozitár Nginx. Postupovať by sme mali podľa návodu na nginx webe. Najprv nainštalujeme (je možné že už to máme nainštalované)

sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring

Poznámka: Ja už mám Nginx nainštalovaný budem len aktualizovať na ver. 1.25.2

Importujeme oficiálny podpisový kľúč nginx, aby apt mohol overiť pravosť balíkov.

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Skontrolujeme, či stiahnutý súbor obsahuje správny kľúč:

gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg

Výstup by mal obsahovať cely fingerprint 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 takto:

pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
      573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
uid                      nginx signing key <signing-key@nginx.com>

Teraz môžeme nastaviť aby sa inštalovalo buď zo stabilnej, alebo z hlavnej vetvy. Kedže podpora HTTP/3 je od ver. 1.25.1 a v stabilnej vetve ešte nie je tato ver. tak musíme nastaviť hlavnú vetvu.

Toto je pre stabilnú vetvu, takže mi nepoužijeme tento príkaz, pretože pri písani tohto článku ešte v stabilnej vetve nie je nginx s podporou HTTP/3

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Až tento príkaz pre hlavnú vetvu obsahuje nginx s podporou HTTP/3

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Nastavíme aby sa uprednostnila inštalácia z nginx úložiska pred inštaláciou poskytovateľa distribúcie (čiže úložisko pre Debian 12)

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

Aktualizujeme zmeny, ktoré sme pridali a môžete inštalovať

sudo apt update
sudo apt install nginx

Počas inštalácie uvidíme ničo také (upozorňujem, že táto hláška sa objaví, len na systéme, kde už je nainštalovaná strašia ver. Nginx).

Configuration file '/etc/nginx/nginx.conf'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** nginx.conf (Y/I/N/O/D/Z) [default=N] ?

Táto hláška hovorí o tom že konfiguračný súbor je iný ako pôvodný a my sa máme rozhodnúť či nainštalujeme nový, alebo ponecháme pôvodný (príp. skontrolovať rozdieli). Predvolene je vybraté „N“, takže môžeme pokračovať s pôvodným. Po nainštalovaní povolíme službu nginx v systemd a na manipuláciu budeme používať štandardne príkazy

sudo systemctl enable nginx

sudo systemctl status nginx
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl reload nginx

Či je v Nginx určite dostupný aj modul pre HTTP/3 sa môžeme presvedčiť napr. takto

sudo nginx -V 2>&1 | tr ' ' '\n' | grep 'http_'

Všimnime si posledný modul. To znamená, že binárka je skompilovaná s HTTP/3 modulom.

--with-http_addition_module
--with-http_auth_request_module
--with-http_dav_module
--with-http_flv_module
--with-http_gunzip_module
--with-http_gzip_static_module
--with-http_mp4_module
--with-http_random_index_module
--with-http_realip_module
--with-http_secure_link_module
--with-http_slice_module
--with-http_ssl_module
--with-http_stub_status_module
--with-http_sub_module
--with-http_v2_module
--with-http_v3_module

Ak sme Nginx už spustili, tak všetky konfiguračné súbory by mali byť Nginxom načítane a malo by to všetko fungovať. Môžeme sa však stretnúť minimálne z jednou zastaralou direktívou a to HTTP2. Po štarte nginx, resp. po reloade môžeme vidieť tieto hlášky a to pre každý konfiguračný súbor (čiže ich tam môže byť viac).

Sep 07 12:37:59 debian-12-nginx-proxy nginx[815]: nginx: [warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead in /etc/nginx/sites-enabled/www.example.com:5
Sep 07 12:37:59 debian-12-nginx-proxy nginx[815]: nginx: [warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead in /etc/nginx/sites-enabled/www.example.com:34

Spôsobené je to touto smernicou

listen 443 ssl http2;

Musíme ju nahradiť týmto

listen 443 ssl;
http2 on;

Po tejto zmene v každom konfiguračnom súbore už po reloade nedostaneme žiadne škaredé hlášky

sudo nginx -t && sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Ak by sme nechceli používať HTTP/3, tak jednoducho už nič nemusíme robiť a naďalej budme fungovať s HTTP/2. Avšak naším cieľom je HTTP/3.

Sem ešte vsuniem jednu drobnú ale dôležitú poznámku. Aby sme sa po konfigurácii servera zbytočne netrápili, prečo nám nefunguje HTTP/3, tak na našom routery/firewalle musíme v časti NAT okrem TCP povoliť tiež UDP protokol na porte 443.

Musím napísať, že podľa manuálu na nginx webe to mala byť jednoduchá záležitosť, ale dosť som sa s tým potrápil. Z návodu plynie, že pre základnú implementáciu HTTP/3 sú potrebné 3 smernice

listen 443 quic reuseport;
ssl_protocols TLSv1.3;
add_header Alt-Svc 'h3=":$server_port"; ma=86400';

Vzhľadom k tomu, že smernicu ssl_protocols už mám globálne v hlavnom config file, tak som dostal upozornenie na duplicitu. Zúžilo sa to na smernicu listen a add_header. V každom konfiguračnom súbore mám 2 server bloky. Najprv redirect z non www na www a potom na https. Čiže do oboch blokov som nasadil (vrátane HTTP hlavičky)

    listen 443 quic reuseport; # QUIC
    listen 443 ssl;            # TCP
    add_header Alt-Svc 'h3=":$server_port"; ma=86400'; 

A vždy som dostal po kontrole konfigurácie hlášku

nginx: [emerg] duplicate listen options for 0.0.0.0:443 in /etc/nginx/sites-enabled/www.example.com:5
nginx: configuration file /etc/nginx/nginx.conf test failed

OK, odstránením smernice z prvého server blocku

    listen 443 quic reuseport; # QUIC

to už fungovalo bez problémov s HTTP/3. Konfiguráciu som si uložil a na editáciu ma čakali ďalšie konfiguráky pre ďalšie domény. Najprv som skúsil upraviť druhý konfigurák a znova som dostal podobnú hlášku

nginx: [emerg] duplicate listen options for 0.0.0.0:443 in /etc/nginx/sites-enabled/www.example.com:5
nginx: configuration file /etc/nginx/nginx.conf test failed

Začal som teda pátrať, prečo sa to tak správa. Musím napísať, že fakt som sa s tým pár hodín trápil až som našiel na stackoverflow, že problém sa dá veľmi jednoducho vyriešiť. Smernicu s reuseport je možné použiť v celej konfigurácii len raz. To znamená, že v jednom konfiguráku to nastavíme s

    # HTTP/3 support
    listen 443 quic reuseport;
    http2 on;
    http3 on;
    http3_hq on;
    quic_retry on;
    listen 443 ssl;

V ďalších server blockoch už pridáme len

    #HTTP/3 support 
    listen 443 ssl;
    listen 443 quic;

    # HTTP/3 Header     
    add_header Alt-Svc 'h3=":$server_port"; ma=86400';
    add_header x-quic 'h3';
    add_header Alt-Svc 'h3-29=":$server_port"';

Vidíme, že reuseport tam už nie je. Čiže každý ďalší konfigurák by mohol vyzerať nejak takto. Je to klasická konfigurácia pre reverzný proxy server obohatená o bezpečnostné HTTP hlavičky a pridaná podpora pre HTTP/3.

server {
    server_name example.com;
    return 301 http://www.example.com$request_uri;
 
    #HTTP/3 support 
    listen 443 ssl;
    listen 443 quic;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
 
    # HTTP Header 
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Referrer-Policy                      "no-referrer"   always;
    add_header X-Content-Type-Options               "nosniff"       always;
    add_header X-Download-Options                   "noopen"        always;
    add_header X-Frame-Options                      "SAMEORIGIN"    always;
    add_header X-Permitted-Cross-Domain-Policies    "none"          always;
    add_header X-Robots-Tag                         "none"          always;
    add_header X-XSS-Protection                     "1; mode=block" always;
    add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';" always;
    add_header Permissions-Policy "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()";
     
    # HTTP/3 Header     
    add_header Alt-Svc 'h3=":$server_port"; ma=86400';
    add_header x-quic 'h3';
    add_header Alt-Svc 'h3-29=":$server_port"';
}
   
server {
    server_name www.example.com;
   
    location / {
        proxy_pass http://192.168.20.11;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
  
    #HTTP/3 support
    listen 443 ssl;
    listen 443 quic;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
  
    # HTTP Header    
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Referrer-Policy                      "no-referrer"   always;
    add_header X-Content-Type-Options               "nosniff"       always;
    add_header X-Download-Options                   "noopen"        always;
    add_header X-Frame-Options                      "SAMEORIGIN"    always;
    add_header X-Permitted-Cross-Domain-Policies    "none"          always;
    add_header X-Robots-Tag                         "none"          always;
    add_header X-XSS-Protection                     "1; mode=block" always;
    add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';" always;
    add_header Permissions-Policy "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()";
     
    # HTTP/3 Header    
    add_header Alt-Svc 'h3=":$server_port"; ma=86400';
    add_header x-quic 'h3';
    add_header Alt-Svc 'h3-29=":$server_port"'; 
}
  
server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
  
  
    listen 80;
    server_name example.com;
    return 404; # managed by Certbot
  
  
}
   
server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
  
  
    listen 80;
    server_name www.example.com;
    return 404; # managed by Certbot
  
  
}

Či skutočne používame HTTP/3 protokol sa môžeme presvedčiť vo webovom prehliadači, napr. Mozilla, F12 (vývojárska konzola), kde si necháme zobraziť odpovede zo servera (v časti Network -> Headers).

HTTP/3 200 OK
server: nginx/1.25.2
date: Mon, 18 Sep 2023 12:42:25 GMT
content-type: text/css
content-length: 2872
last-modified: Sat, 12 Aug 2023 10:42:00 GMT
etag: "424f-602b77aa4ebf8-gzip"
accept-ranges: bytes
vary: Accept-Encoding
content-encoding: gzip
strict-transport-security: max-age=31536000; includeSubDomains
referrer-policy: no-referrer
x-content-type-options: nosniff
x-download-options: noopen
x-frame-options: SAMEORIGIN
x-permitted-cross-domain-policies: none
x-robots-tag: none
x-xss-protection: 1; mode=block
content-security-policy: default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';
permissions-policy: geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()
alt-svc: h3=":443"; ma=86400
x-quic: h3
alt-svc: h3-29=":443"

Ale tiež keď pôjdme na http3check

Záver

Musím sa priznať, že prechod na HTTP/3 ma trocha potrápil, ale nakoniec to snáď dopadlo dobre. Všetky weby ktoré hostujem na mojich serveroch bežia už s protokolom HTTP/3. Už z princípu by mal byť HTTP/3 rýchlejší ako HTTP/2 (1.1), pretože funguje na UDP. Bežný užívateľ si to až tak veľmi nevšimne, pretože sa jedná o milisekundy. Netvrdím, že moja konfigurácia je ideálna, určite by sa dala ešte vylepšiť, ale v rámci možnosti mi to funguje celkom slušne. Zatiaľ to mám nasadené len niekoľko dní, takže časom to možno budem odlaďovať.

Použitá literatúra

Leave a Reply

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *