HTTP/3 i QUIC - konfiguracja na Nginx i Caddy
Opublikowano: 10 kwietnia 2026 · Kategoria: Hosting / Wydajność
HTTP/3 to trzecia główna wersja protokołu HTTP, oparta na QUIC (Quick UDP Internet Connections) zamiast TCP. Eliminuje klasyczny problem "head-of-line blocking", przyspiesza ładowanie stron na wolnych połączeniach i wspiera migrację połączeń bez zrywania sesji. Oto jak go uruchomić.
Ewolucja HTTP: 1.1 → 2 → 3
| Cecha | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Transport | TCP | TCP | UDP (QUIC) |
| Multiplexing | Brak (pipelining) | Tak (strumienie) | Tak (niezależne strumienie) |
| Head-of-line blocking | Tak (poziom połączenia) | Tak (poziom TCP) | Nie |
| TLS | Opcjonalne (TLS 1.2/1.3) | Wymagane (TLS 1.2+) | Wbudowane TLS 1.3 |
| Handshake RTT | 1-3 RTT | 1-2 RTT | 0-1 RTT (0-RTT resumption) |
| Connection migration | Nie | Nie | Tak (zmiana IP bez przerwy) |
Kluczowa różnica: QUIC działa na UDP, więc każdy strumień HTTP/3 jest niezależny. Utrata pakietu w jednym strumieniu (np. obraz) nie wstrzymuje pobierania innych zasobów (CSS, JS) - tak jak to dzieje się w HTTP/2 na TCP.
Opcja 1: Caddy - HTTP/3 bez konfiguracji
Caddy to najprostszy sposób na HTTP/3. Od wersji 2.6 QUIC jest domyślnie włączony - wystarczy skonfigurować domenę z HTTPS:
# Instalacja Caddy (Ubuntu/Debian) apt install -y debian-keyring debian-archive-keyring apt-transport-https curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list apt update && apt install caddy
# /etc/caddy/Caddyfile
example.com {
root * /var/www/html
file_server
# HTTP/3 jest włączone automatycznie - nic nie musisz konfigurować!
# Caddy dodaje nagłówek Alt-Svc: h3=":443"; ma="2592000"
# Opcjonalnie: wymuś HTTP/3 przez nagłówek
header Alt-Svc `h3=":443"; ma=86400`
} systemctl reload caddy # Weryfikacja curl -sI https://example.com | grep -i alt-svc # Oczekiwane: Alt-Svc: h3=":443"; ma=2592000
Ważne: QUIC używa UDP port 443. Upewnij się, że firewall przepuszcza UDP 443
(nie tylko TCP): ufw allow 443/udp.
Caddy HTTP/3 - kiedy jednak trzeba coś ustawić
W Caddy 2.6 i nowszych HTTP/3 jest domyślne, więc w większości przypadków jedyną przyczyną
braku "h3" jest zablokowany UDP. Jawna konfiguracja protokołów przydaje się, gdy chcesz
HTTP/3 świadomie wyłączyć (np. do testów porównawczych) albo gdy dziedziczysz Caddyfile, w
którym ktoś ograniczył listę protokołów. Służy do tego blok servers w opcjach globalnych:
# /etc/caddy/Caddyfile - opcje globalne na samej górze pliku
{
servers {
protocols h1 h2 h3 # domyślny zestaw; usuń h3, aby wyłączyć HTTP/3
}
}
example.com {
reverse_proxy 127.0.0.1:3000
} Dwie pułapki spotykane najczęściej:
- Caddy w Dockerze - samo
"443:443"w sekcjiportspublikuje tylko TCP. Dla HTTP/3 potrzebne jest dodatkowe mapowanie"443:443/udp", inaczej kontener nasłuchuje na UDP, ale ruch z zewnątrz do niego nie dociera. - Ostrzeżenie o buforach UDP - Caddy (biblioteka quic-go) może logować komunikat
"failed to sufficiently increase receive buffer size". HTTP/3 działa dalej, ale przy większym
ruchu warto podnieść limity jądra:
sysctl -w net.core.rmem_max=7500000orazsysctl -w net.core.wmem_max=7500000(na stałe w/etc/sysctl.d/).
Opcja 2: Nginx z QUIC (mainline >= 1.25.0)
Od Nginx 1.25.0 (mainline) wbudowana jest obsługa HTTP/3 i QUIC bez patchowania. Wymaga OpenSSL 3.x lub BoringSSL:
# Instalacja Nginx mainline (Ubuntu) curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" | tee /etc/apt/sources.list.d/nginx.list apt update && apt install nginx # Sprawdzenie wersji i obsługi HTTP/3 nginx -V 2>&1 | grep -o 'with-http_v3_module'
# /etc/nginx/sites-available/http3-example.conf
server {
listen 443 ssl;
listen 443 quic reuseport; # HTTP/3 na UDP
listen [::]:443 ssl;
listen [::]:443 quic reuseport;
server_name example.com;
http2 on; # HTTP/2 dla klientów TCP (Nginx 1.25.1+)
http3 on; # domyślnie włączone, wpisane dla czytelności
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3; # QUIC i tak negocjuje TLS 1.3
# Informuj przeglądarkę o HTTP/3
add_header Alt-Svc 'h3=":443"; ma=86400' always;
add_header X-Protocol $http3 always; # "h3" jeśli żądanie przyszło po HTTP/3
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# Przekierowanie HTTP → HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
} nginx -t && systemctl reload nginx # Sprawdzenie logów HTTP/3 - dodaj $http3 do log_format # log_format main '$remote_addr [$time_local] "$request" $status $http3'; # access_log /var/log/nginx/access.log main;
Nginx QUIC i HTTP/3 - dyrektywy, wiele vhostów i 0-RTT
Obsługę QUIC i HTTP/3 zapewnia moduł ngx_http_v3_module, dostępny od Nginx
1.25.0 (nadal oznaczany w dokumentacji jako eksperymentalny). Moduł musi być wkompilowany -
jeśli nginx -V nie pokazuje --with-http_v3_module, parametr
quic w dyrektywie listen zakończy się błędem przy
nginx -t. Pakiety mainline z repozytorium nginx.org mają ten moduł, starsze
pakiety z repozytoriów dystrybucji często nie.
reuseport tylko raz na port
Najczęstszy błąd przy kilku stronach na jednym serwerze to skopiowanie
listen 443 quic reuseport; do każdego vhosta. Parametr
reuseport (podobnie jak default_server) może wystąpić tylko raz
dla danej pary adres:port, w przeciwnym razie Nginx zgłosi duplikat opcji listen. W
pozostałych blokach server wystarczy samo quic:
# Pierwszy vhost (np. domyślny)
server {
listen 443 ssl;
listen 443 quic reuseport;
server_name example.com;
# ...
}
# Każdy kolejny vhost na tym samym IP i porcie
server {
listen 443 ssl;
listen 443 quic; # bez reuseport
server_name sklep.example.com;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
# ...
} Alt-Svc w Nginx: dziedziczenie add_header
Przeglądarka przechodzi na HTTP/3 dopiero wtedy, gdy zobaczy nagłówek
Alt-Svc: h3=":443"; ma=86400 (ma to czas w sekundach, 86400 = doba). W Nginx łatwo
go zgubić: jeśli w bloku location pojawi się jakakolwiek własna dyrektywa
add_header (np. Cache-Control dla statyków), nagłówki z poziomu server nie są dziedziczone
i Alt-Svc znika z odpowiedzi. Rozwiązanie to powtórzenie Alt-Svc w takim location albo trzymanie
wspólnych nagłówków w pliku dołączanym przez include. Bez parametru always nagłówek nie jest dodawany do odpowiedzi z błędem (np. 404, 500).
0-RTT (ssl_early_data) i dodatkowe dyrektywy QUIC
Dyrektywa ssl_early_data on; włącza 0-RTT: powracający klient wysyła żądanie już
w pierwszym pakiecie. Dane wysłane w 0-RTT mogą zostać powtórzone przez atakującego (replay attack),
dlatego dokumentacja Nginx zaleca przekazanie zmiennej
$ssl_early_data do aplikacji, która może wtedy odrzucić nieidempotentne żądania (np.
POST składający zamówienie) kodem 425 Too Early. Jeśli aplikacja tego nie obsługuje, bezpieczniej
zostawić 0-RTT wyłączone. Przy buildzie z OpenSSL (warstwa zgodności QUIC) 0-RTT w QUIC i tak
nie jest dostępne - wymaga bibliotek takich jak BoringSSL, LibreSSL lub QuicTLS.
ssl_early_data on; proxy_set_header Early-Data $ssl_early_data; # "1" gdy żądanie przyszło w 0-RTT quic_retry on; # walidacja adresu klienta (utrudnia spoofing IP) quic_gso on; # wysyłanie pakietów z segmentation offload (Linux)
Apache a HTTP/3 i QUIC - realne opcje
Apache httpd (gałąź 2.4) nie ma natywnej obsługi HTTP/3 ani QUIC - nie istnieje moduł
analogiczny do mod_http2, który dałoby się po prostu włączyć. Jeśli strona stoi
na Apache, a chcesz serwować ją po HTTP/3, masz trzy praktyczne drogi:
- Reverse proxy przed Apache - Caddy albo Nginx przyjmuje ruch na 443 (TCP i UDP), obsługuje TLS i HTTP/3, a do Apache przekazuje żądania lokalnie po HTTP/1.1. Apache przenosisz na port wewnętrzny, np. 127.0.0.1:8080. Konfiguracja .htaccess dalej działa, bo żądania nadal obsługuje Apache.
- CDN z HTTP/3 - np. Cloudflare z włączoną opcją HTTP/3 (with QUIC). Przeglądarka łączy się z CDN po HTTP/3, a CDN z Twoim serwerem po HTTP/1.1 lub HTTP/2, więc na serwerze z Apache nie trzeba niczego zmieniać ani otwierać UDP 443.
- Zamiana Apache na LiteSpeed - LiteSpeed Web Server (Enterprise) obsługuje HTTP/3 i czyta konfigurację Apache oraz pliki .htaccess, dlatego wielu dostawców hostingu współdzielonego używa go zamiast Apache. Darmowy OpenLiteSpeed też obsługuje HTTP/3, ale zgodność z .htaccess jest w nim tylko częściowa (głównie reguły rewrite).
Przykład najprostszego wariantu z Caddy przed Apache na tym samym VPS:
# 1. Apache tylko lokalnie - /etc/apache2/ports.conf
Listen 127.0.0.1:8080
# oraz w vhoście: <VirtualHost 127.0.0.1:8080>
# 2. Prawdziwe IP odwiedzających w logach Apache
a2enmod remoteip
# w konfiguracji Apache:
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1
# 3. /etc/caddy/Caddyfile - TLS, HTTP/2 i HTTP/3 po stronie Caddy
example.com {
reverse_proxy 127.0.0.1:8080
} Uwaga: aplikacja za proxy (np. WordPress) widzi połączenie z Caddy po zwykłym
HTTP. Caddy przekazuje nagłówek X-Forwarded-Proto: https - aplikacja musi go uwzględniać,
inaczej możesz dostać pętlę przekierowań na HTTPS.
Konfiguracja firewalla - UDP 443 obowiązkowe
# UFW ufw allow 443/tcp ufw allow 443/udp # QUIC wymaga UDP! # iptables (alternatywnie) iptables -A INPUT -p udp --dport 443 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT
Uwaga na dostawców chmury: AWS, GCP i Azure mają osobne Security Groups / Firewall rules. Musisz dodać regułę UDP 443 w panelu dostawcy, nie tylko w systemowym UFW.
Port HTTP/3 i QUIC - jak przeglądarka go znajduje
HTTP/3 nie ma osobnego "portu HTTP/3" w rodzaju 8443 - standardowo używa tego samego numeru co HTTPS, czyli 443, tylko po protokole UDP. Przeglądarka nie zgaduje jednak, że serwer mówi po QUIC. Typowa sekwencja wygląda tak:
- Pierwsze połączenie idzie po TCP 443 (HTTP/2 lub HTTP/1.1).
-
Serwer odpowiada nagłówkiem
Alt-Svc: h3=":443"; ma=86400- to on wskazuje port UDP, na którym działa HTTP/3. -
Przeglądarka zapamiętuje tę informację na czas z
mai kolejne żądania próbuje wysyłać po QUIC. Gdy UDP nie odpowiada, wraca do TCP.
Wartość w Alt-Svc może wskazywać inny port (np. h3=":8443"), ale wtedy to ten
port UDP musi być otwarty i nasłuchiwać. Krok z pierwszym połączeniem po TCP można skrócić
rekordem DNS typu HTTPS (np. alpn="h3,h2"), z którego przeglądarki obsługujące
ten typ rekordu dowiadują się o HTTP/3 jeszcze przed połączeniem. Sprawdzenie, czy serwer
faktycznie nasłuchuje na UDP i czy ruch przechodzi przez firewall:
# Czy Nginx/Caddy nasłuchuje na UDP 443? ss -ulnp | grep ':443' # nftables (gdy nie używasz UFW; tabela inet filter musi istnieć) nft add rule inet filter input udp dport 443 accept # Stan reguł UFW ufw status | grep 443
Pamiętaj o trzech warstwach: firewall systemowy, firewall w panelu dostawcy VPS (np. Hetzner Cloud Firewall, Security Groups w AWS) i ewentualnie mapowanie portów w Dockerze. Szczegóły reguł znajdziesz w poradniku UFW - konfiguracja firewalla na VPS.
Testowanie HTTP/3
# curl z HTTP/3 (wymaga curl 7.66+ z obsługą QUIC) curl --http3 -I https://example.com # Sprawdź nagłówek Alt-Svc curl -sI https://example.com | grep Alt-Svc # Chrome DevTools - weryfikacja # 1. F12 → Network → przeładuj stronę # 2. Kliknij żądanie → Headers → "protocol: h3" # chrome://net-internals/#quic - aktywne sesje QUIC # chrome://net-internals/#http2 - wszystkie protokoły # Narzędzie online # https://http3check.net/?host=example.com
HTTP/3 nie działa - typowe przyczyny
- Brak nagłówka Alt-Svc - serwer nasłuchuje na UDP, ale przeglądarka o tym nie
wie. Sprawdź
curl -sI https://example.com | grep -i alt-svcdla kilku adresów (strona główna, plik statyczny, 404), bo w Nginx nagłówek bywa gubiony w poszczególnych blokach location. - Zablokowany UDP 443 - nagłówek Alt-Svc jest, ale w DevTools wciąż widać "h2".
Najczęściej brakuje reguły w firewallu dostawcy VPS albo mapowania
443/udpw Dockerze. - Pierwsze żądanie po h2 to norma - bez rekordu DNS HTTPS pierwsze wczytanie strony zawsze idzie po TCP. Oceniaj protokół po przeładowaniu strony, a w DevTools włącz kolumnę Protocol (prawy klik na nagłówek tabeli w zakładce Network) i szukaj wartości "h3".
- Sieci blokujące QUIC - część sieci firmowych, publicznych hotspotów i urządzeń pośredniczących (middleboxy, firewalle UTM) blokuje lub ogranicza UDP 443. Przeglądarka wtedy po cichu wraca do HTTP/2. To zachowanie zamierzone, nie błąd serwera - dlatego TCP 443 z HTTP/2 musi zawsze działać równolegle.
- curl bez obsługi HTTP/3 - curl z repozytoriów wielu dystrybucji nie ma wkompilowanego
HTTP/3 i odrzuci flagę
--http3. Sprawdź, czycurl -Vpokazuje "HTTP3" w liście Features; jeśli nie, testuj przez przeglądarkę lub http3check.net. - Strona za CDN - gdy domena jest proxowana przez Cloudflare, o HTTP/3 dla odwiedzających decyduje ustawienie w panelu CDN, a nie konfiguracja serwera. Więcej o tym układzie w artykule CDN w hostingu - Cloudflare.
Wskazówka: po zmianie konfiguracji przeglądarka może przez pewien czas pamiętać stare informacje Alt-Svc albo nieudaną próbę QUIC. Do testów używaj okna prywatnego lub innej przeglądarki.
Kiedy HTTP/3 najbardziej pomaga?
- Duże liczby zasobów - strony z 50+ żądaniami JS/CSS/obrazów - brak HoL blockingu przyspiesza ich równoległe pobieranie.
- Użytkownicy mobilni - słabe, stratne połączenia 4G/5G - QUIC lepiej radzi sobie z utraconymi pakietami.
- Aplikacje real-time - czat, streaming - 0-RTT resumption skraca czas nawiązywania sesji po przerwie.
- Globalna publiczność - użytkownicy daleko od serwera - mniej RTT handshake.
- Słabsza sieć - np. publiczne WiFi - QUIC jest bardziej odporny na packet loss niż TCP.
Kiedy zysk jest mały: serwer lokalny, stabilna sieć, mała liczba zasobów na stronie, już skonfigurowane HTTP/2 Push lub preload. W tych przypadkach HTTP/3 daje <5% poprawy.
LiteSpeed - HTTP/3 gotowe na hostingu
Jeśli używasz hostingu współdzielonego z LiteSpeed (np. LH.pl, CyberFolks), HTTP/3 i QUIC mogą być już aktywne po stronie serwera - bez żadnej konfiguracji z Twojej strony. Sprawdź nagłówki odpowiedzi:
curl -sI https://twojastrona.pl | grep -i 'alt-svc\|server\|x-powered' # LiteSpeed Enterprise: server: LiteSpeed, alt-svc: h3=":443"