HTTP/3 es la versión de HTTP que funciona sobre QUIC, un transporte cifrado que va sobre UDP en lugar de TCP. Integra TLS 1.3 en el propio transporte, establece conexiones con menos viajes de ida y vuelta y evita que un paquete perdido bloquee todas las peticiones de la conexión, lo que se nota sobre todo en redes móviles o con pérdidas. En esta guía activarás HTTP/3 en Nginx en Ubuntu 24.04 usando el paquete oficial de nginx.org, abrirás el puerto UDP necesario y comprobarás que los navegadores lo usan. Al final verás cómo hacerlo con Caddy, que lo trae activado de serie.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Un dominio (your_domain en los ejemplos) con un registro DNS A apuntando a la IP pública del servidor.
  • Los puertos 80/TCP, 443/TCP y 443/UDP abiertos. Si tienes un firewall de red además de UFW, abre también el UDP 443 allí.

Cómo se negocia HTTP/3

Un navegador no intenta HTTP/3 a ciegas. La primera conexión va por TCP con HTTP/2 o HTTP/1.1, y el servidor anuncia en la cabecera Alt-Svc que también atiende HTTP/3 en el puerto UDP 443:

alt-svc: h3=":443"; ma=86400

A partir de ahí el navegador intenta QUIC y, si el UDP está bloqueado, sigue usando TCP sin que el usuario note nada. Por eso HTTP/3 siempre se configura junto a HTTPS por TCP, nunca en su lugar.

Paso 1: Instalar Nginx desde el repositorio de nginx.org

Si tienes instalado el Nginx de Ubuntu, haz copia de tu configuración y elimínalo, porque los paquetes de nginx.org lo sustituyen:

sudo cp -r /etc/nginx ~/nginx-backup
sudo apt remove nginx nginx-common

Instala las herramientas para añadir el repositorio y descarga la clave de firma:

sudo apt update
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg

Añade el repositorio de la rama estable:

echo "deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list

Da prioridad a estos paquetes frente a los de Ubuntu para que una actualización no te devuelva a la versión 1.24:

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

Instala Nginx y arráncalo:

sudo apt update
sudo apt install nginx
sudo systemctl enable --now nginx

Comprueba que la versión es 1.25 o superior y que el módulo HTTP/3 está compilado:

nginx -V 2>&1 | grep -o 'nginx/[0-9.]*\|with-http_v3_module'
nginx/1.28.0
with-http_v3_module

El número de versión que veas será el de la rama estable vigente. Lo importante es que aparezca with-http_v3_module.

Paso 2: Abrir los puertos en el firewall

QUIC usa UDP, así que además de los puertos TCP habituales necesitas el UDP 443:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
443/udp                    ALLOW       Anywhere
...

Olvidar el UDP 443 es la causa más habitual de que HTTP/3 "no funcione": el sitio carga igual por TCP y el fallo pasa desapercibido.

Paso 3: Obtener un certificado TLS

QUIC exige TLS 1.3 y un certificado válido. Instala Certbot:

sudo apt install certbot python3-certbot-nginx

Crea un bloque HTTP mínimo para tu dominio. Primero desactiva el sitio por defecto del paquete:

sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.disabled
sudo nano /etc/nginx/conf.d/your_domain.conf
server {
    listen 80;
    listen [::]:80;
    server_name your_domain;
    root /usr/share/nginx/html;
}

Recarga Nginx y pide el certificado. Con certonly Certbot solo obtiene el certificado y no modifica tu configuración, que escribirás a mano en el siguiente paso:

sudo nginx -t && sudo systemctl reload nginx
sudo certbot certonly --nginx -d your_domain
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/your_domain/privkey.pem

Certbot instala un temporizador de systemd que renueva el certificado automáticamente.

Paso 4: Configurar HTTP/3 en Nginx

Sustituye el contenido de /etc/nginx/conf.d/your_domain.conf:

sudo nano /etc/nginx/conf.d/your_domain.conf
server {
    listen 443 quic reuseport;
    listen [::]:443 quic reuseport;
    listen 443 ssl;
    listen [::]:443 ssl;

    http2 on;
    server_name your_domain;
    root /usr/share/nginx/html;

    ssl_certificate     /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    add_header Alt-Svc 'h3=":443"; ma=86400' always;
}

server {
    listen 80;
    listen [::]:80;
    server_name your_domain;
    return 301 https://$host$request_uri;
}

Qué hace cada parte:

  • listen 443 quic reuseport: escucha QUIC en el UDP 443. reuseport reparte los paquetes entre los procesos worker de forma que cada conexión QUIC llegue siempre al mismo worker. Solo puede aparecer en un bloque server por cada combinación de dirección y puerto: si alojas más dominios, en los demás pon listen 443 quic; sin reuseport.
  • listen 443 ssl y http2 on: mantienen HTTPS por TCP para la primera conexión y para clientes sin HTTP/3.
  • ssl_protocols: QUIC usa siempre TLS 1.3, TLSv1.2 queda solo para clientes TCP antiguos.
  • add_header Alt-Svc: anuncia HTTP/3. always hace que se envíe también en respuestas de error. ma=86400 indica al navegador que recuerde el anuncio durante un día.

Comprueba la sintaxis y recarga:

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

Paso 5: Comprobar que HTTP/3 funciona

Primero confirma que Nginx escucha en el UDP 443:

sudo ss -ulpn 'sport = :443'
State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
UNCONN 0      0            0.0.0.0:443        0.0.0.0:*     users:(("nginx",pid=2311,fd=6),...)
UNCONN 0      0               [::]:443           [::]:*     users:(("nginx",pid=2311,fd=7),...)

Después comprueba desde tu equipo que el servidor envía la cabecera Alt-Svc:

curl -sI https://your_domain | grep -i alt-svc
alt-svc: h3=":443"; ma=86400

El curl de Ubuntu 24.04 no está compilado con soporte HTTP/3, así que la prueba final se hace con el navegador:

  1. Abre https://your_domain en Chrome o Firefox y abre las herramientas de desarrollo (F12), pestaña Red.
  2. Haz clic derecho en la cabecera de las columnas y activa Protocolo.
  3. Recarga la página dos veces. La primera carga puede ir por h2, porque es la que recibe el anuncio Alt-Svc. En las siguientes debe aparecer h3.

También puedes registrar el protocolo en el log de acceso. Añade este formato en el bloque http de /etc/nginx/nginx.conf:

log_format proto '$remote_addr [$time_local] "$request" $status $server_protocol';

Y dentro del bloque server de tu dominio:

access_log /var/log/nginx/your_domain.access.log proto;

Tras recargar Nginx y navegar por el sitio, las peticiones por QUIC aparecen como HTTP/3.0:

sudo tail -n 3 /var/log/nginx/your_domain.access.log
203.0.113.25 [25/Sep/2026:10:14:02 +0000] "GET / HTTP/3.0" 200 HTTP/3.0
203.0.113.25 [25/Sep/2026:10:14:02 +0000] "GET /favicon.ico HTTP/3.0" 404 HTTP/3.0

Alternativa: HTTP/3 con Caddy

Caddy activa HTTP/3 automáticamente en todos los sitios con HTTPS, sin configuración adicional. Es una alternativa a Nginx, no un complemento: ambos usan los puertos 80 y 443, así que úsalo en un servidor sin Nginx o detén Nginx antes con sudo systemctl disable --now nginx. Instálalo desde su repositorio oficial:

sudo apt install debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Edita /etc/caddy/Caddyfile con un sitio mínimo que sirve la página de bienvenida incluida en el paquete:

sudo nano /etc/caddy/Caddyfile
your_domain {
    root * /usr/share/caddy
    file_server
}

Recarga Caddy. Obtendrá el certificado de Let's Encrypt, servirá HTTP/1.1, HTTP/2 y HTTP/3 y enviará la cabecera Alt-Svc por sí mismo:

sudo systemctl reload caddy

Caddy usa la biblioteca quic-go, que recomienda buffers UDP mayores que los que trae el kernel por defecto. Si ves el aviso failed to sufficiently increase receive buffer size en journalctl -u caddy, auméntalos:

printf "net.core.rmem_max=7500000\nnet.core.wmem_max=7500000\n" | sudo tee /etc/sysctl.d/60-quic-buffers.conf
sudo sysctl --system
sudo systemctl restart caddy

Recuerda abrir también el UDP 443 con UFW, igual que en el paso 2.

Solución de problemas

  • El navegador siempre usa h2. Comprueba que el UDP 443 está abierto en UFW y en cualquier firewall de red, que la cabecera Alt-Svc llega (el curl -sI del paso 5) y que no hay otro proceso escuchando en el UDP 443. Algunas redes corporativas bloquean QUIC; prueba desde otra conexión.
  • nginx: [emerg] invalid parameter "quic". Estás ejecutando un Nginx sin el módulo HTTP/3, normalmente el de Ubuntu. Revisa nginx -V y el pin del paso 1.
  • duplicate listen options for 0.0.0.0:443. Has puesto reuseport en más de un bloque server para el mismo puerto. Déjalo solo en uno.
  • HTTP/3 funciona por IPv4 pero no por IPv6. Falta la línea listen [::]:443 quic o la regla de UFW para IPv6. Revisa con sudo ufw status que aparece 443/udp (v6).

Conclusión

Tu servidor atiende ahora HTTP/3 sobre QUIC en el UDP 443 y lo anuncia con Alt-Svc, manteniendo HTTP/2 por TCP para los clientes que no pueden usar UDP. Como siguientes pasos, puedes activar HSTS cuando todo tu sitio funcione por HTTPS, usar el log con $server_protocol para medir qué porcentaje de tráfico llega por HTTP/3 y aplicar la misma configuración a los bloques server que hagan de proxy inverso hacia tus aplicaciones.