Activar HTTP/3 en nginx paso a paso

HTTP/3 funciona sobre QUIC y UDP en lugar de TCP. En nginx bastan unas pocas líneas; la mayoría de los problemas vienen del cortafuegos o de una cabecera Alt-Svc ausente.

Requisitos

Con el OpenSSL normal, nginx usa una capa de compatibilidad para QUIC. Funciona, pero no admite 0-RTT (early data). Para la mayoría de las webs da igual.

La configuración

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    listen 443 quic reuseport;
    listen [::]:443 quic reuseport;

    http2 on;
    http3 on;
    server_name example.com;

    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;

    # avisar a los navegadores de que hay HTTP/3
    add_header Alt-Svc 'h3=":443"; ma=86400' always;
}

Importante: reuseport solo puede aparecer una vez por dirección y puerto. Con varios bloques server, solo el primero lleva listen 443 quic reuseport; y los demás solo listen 443 quic;. Si no, nginx no arranca y avisa de «duplicate listen options».

Después nginx -t y systemctl reload nginx. El generador de configuración TLS crea una configuración completa y segura con HSTS (opción «HTTP/3»).

Abrir UDP 443

El motivo más habitual por el que HTTP/3 «no funciona»: el cortafuegos solo deja pasar TCP. Los navegadores vuelven entonces en silencio a HTTP/2, sin mensaje de error.

# ufw
sudo ufw allow 443/udp
# firewalld
sudo firewall-cmd --permanent --add-port=443/udp && sudo firewall-cmd --reload
# nftables (ejemplo)
nft add rule inet filter input udp dport 443 accept

Piensa también en cortafuegos fuera del servidor: grupos de seguridad en la nube, el cortafuegos del proveedor o un balanceador de carga delante. Muchos balanceadores no dejan pasar QUIC.

Cómo cambia el navegador a HTTP/3

En la primera visita el navegador se conecta por TCP (HTTP/2). La cabecera Alt-Svc le indica que hay HTTP/3 en el puerto 443, y lo usa desde la siguiente petición. ma=86400 significa que la información vale un día.

Un registro HTTPS en el DNS es más rápido: el navegador conoce HTTP/3 antes de la primera conexión.

example.com.  3600  IN  HTTPS  1 . alpn="h3,h2"

Una trampa típica: add_header deja de heredarse en cuanto un bloque location tiene sus propias líneas add_header, y Alt-Svc falta justo ahí. Solución: poner las cabeceras en un archivo e incluirlo con include en cada bloque afectado.

Probar

# ¿está la cabecera?
curl -sI https://example.com | grep -i alt-svc
# conexión HTTP/3 real (requiere curl con soporte HTTP/3)
curl -sI --http3-only https://example.com
# ¿escucha nginx en UDP?
sudo ss -ulpn | grep ':443'

En las herramientas de desarrollo del navegador, la vista de red tiene la columna «Protocolo»: h3 significa HTTP/3. En la primera petición aún pone h2; cambia tras recargar.

El comprobador HTTP/2 y HTTP/3 revisa ALPN, Alt-Svc y el registro HTTPS y envía un paquete QUIC al servidor.

FAQ

¿Aporta algo HTTP/3 de verdad?

Sobre todo en redes móviles y wifi con pérdida de paquetes: un paquete perdido ya no bloquea todas las peticiones y la conexión se establece antes. En una red cableada rápida la diferencia suele ser mínima.

¿Debo seguir ofreciendo HTTP/2?

Sí. Todo navegador empieza por TCP y algunas redes bloquean UDP. HTTP/3 complementa a HTTP/2, no lo sustituye.

¿Por qué el navegador solo muestra h2 pese a Alt-Svc?

Normalmente UDP 443 no llega (cortafuegos, balanceador, red corporativa) o el navegador aún no tiene la información. Recarga; si no, prueba desde fuera con curl --http3-only.

¿Funciona HTTP/3 con Apache?

No, Apache no admite HTTP/3 por ahora. Lo habitual es poner delante un proxy como nginx, Caddy o una CDN.