Notícias8 min de leitura

QUIC e HTTP/3 na prática: como ativar, testar e depurar a web sobre UDP

O que o QUIC resolve que o TCP não resolvia, como ativar HTTP/3 no Nginx, por que o navegador cai no TCP sem avisar, e as ferramentas para depurar um protocolo que é cifrado até no cabeçalho.

  • quic
  • http3
  • udp
  • tcp
  • tls
  • nginx
  • redes
  • web

Durante trinta anos a web rodou sobre TCP. Isso mudou sem alarde: o QUIC, padronizado no RFC 9000 em 2021, e o HTTP/3 (RFC 9114, 2022) são hoje o caminho padrão entre navegadores modernos e as grandes CDNs. O TCP continua lá, mas cada vez mais como plano B, quando o UDP está bloqueado.

A maioria dos sites ganhou HTTP/3 sem fazer nada, porque a CDN ligou. O problema aparece quando você opera a própria borda: o navegador tenta QUIC, algo no caminho descarta UDP, e ele volta para TCP em silêncio. Tudo funciona, e ninguém sabe se o HTTP/3 está sendo usado.

Este texto cobre o que muda no transporte, como ativar e testar no seu servidor, e como depurar um protocolo que foi desenhado para não ser observável.

O que estava errado com o TCP

O HTTP/2 multiplexou várias requisições numa única conexão TCP. Resolveu o limite de conexões por domínio do HTTP/1.1, mas herdou um problema do TCP: ele entrega bytes em ordem. Se um pacote se perde, tudo que chegou depois fica parado no buffer do kernel esperando a retransmissão, mesmo que pertença a outra requisição. É o head-of-line blocking de transporte.

Em fibra estável, a perda é rara e o efeito é pequeno. Em rede móvel, Wi-Fi cheio ou enlace de longa distância, uma perda trava todas as imagens, scripts e chamadas de API da página ao mesmo tempo.

O outro problema era evolução. O TCP vive no kernel e em milhões de middleboxes que inspecionam e reescrevem cabeçalhos. Mudar o protocolo significa esperar anos por kernels novos e descobrir quais firewalls descartam opções desconhecidas. Na prática, o TCP ossificou.

O que o QUIC faz diferente

O QUIC roda sobre UDP e implementa confiabilidade, controle de congestionamento e criptografia em userspace:

  • Streams independentes. Perda num stream só atrasa aquele stream. As outras requisições seguem.
  • TLS 1.3 embutido (RFC 9001). Transporte e criptografia negociam juntos. Uma conexão nova custa 1 RTT, contra 2 do TCP + TLS 1.3. Uma reconexão pode mandar dados já no primeiro pacote (0-RTT).
  • Connection IDs. A conexão é identificada por um ID, não pela tupla IP/porta. Se o celular sai do Wi-Fi para o 4G, a conexão continua sem novo handshake.
  • Cabeçalhos quase todos cifrados. Middleboxes não conseguem ler nem reescrever números de sequência e confirmações. Isso é proposital: impede a ossificação e permite evoluir o protocolo sem pedir licença à rede.
  • Controle de congestionamento plugável (RFC 9002 define o padrão). Como roda em userspace, trocar o algoritmo é atualizar uma biblioteca, não o kernel.

Como o navegador descobre o HTTP/3

Não existe esquema h3://. O navegador precisa aprender que o servidor fala HTTP/3. Há dois caminhos:

  1. Cabeçalho Alt-Svc. A primeira visita vai por TCP. A resposta inclui Alt-Svc: h3=":443"; ma=86400, e as próximas conexões tentam QUIC.
  2. Registro DNS HTTPS (RFC 9460). O navegador consulta o DNS, vê alpn="h3,h2" e tenta QUIC já na primeira conexão.

Se o QUIC falha, o navegador marca o destino como "sem HTTP/3" por um tempo e segue em TCP. Nenhum erro chega ao usuário. É por isso que muita empresa com UDP bloqueado no firewall nem sabe que tem HTTP/3 desligado na prática.

Ativando no Nginx

O Nginx suporta QUIC e HTTP/3 a partir da série 1.25, desde que compilado com o módulo http_v3 e uma biblioteca TLS com suporte a QUIC (as versões recentes do OpenSSL servem). Confira:

nginx -V 2>&1 | tr ' ' '\n' | grep -E 'http_v3|openssl'

Configuração mínima:

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

    server_name exemplo.com.br;
    ssl_certificate     /etc/ssl/exemplo/fullchain.pem;
    ssl_certificate_key /etc/ssl/exemplo/privkey.pem;
    ssl_protocols TLSv1.3 TLSv1.2;

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

Detalhes que costumam passar batido:

  • reuseport só pode aparecer uma vez por par endereço/porta em toda a configuração. Com vários server no mesmo IP, coloque em apenas um.
  • O listen ... quic é UDP. O listen ... ssl é TCP. Você precisa dos dois: o TCP continua sendo o caminho da primeira visita e o fallback.
  • Abra a porta UDP no firewall. ufw allow 443/tcp não libera UDP. Use ufw allow 443/udp ou a regra equivalente no nftables e no security group da nuvem.

O Caddy liga HTTP/3 por padrão. Em CDNs, costuma ser uma opção no painel, geralmente já ativa.

Testando

Com curl. Nem toda build do curl tem HTTP/3. Confira antes:

curl -V | grep -i http3
curl --http3-only -sI https://exemplo.com.br | head -3

--http3-only falha se o QUIC não fechar, em vez de cair para TCP. É o teste honesto. --http3 sozinho tenta QUIC e aceita fallback, o que esconde o problema.

Com o navegador. Nas DevTools, aba Network, clique com o botão direito no cabeçalho da tabela e habilite a coluna Protocol. Requisições em HTTP/3 aparecem como h3. Lembre que a primeira visita, antes do Alt-Svc, vai ser h2.

UDP chega no servidor?

sudo tcpdump -ni any udp port 443 -c 20

Se os pacotes do cliente aparecem e o servidor não responde, o problema é o Nginx ou o firewall local. Se nada aparece, o UDP está sendo descartado antes.

Depurando um protocolo cifrado

Com TCP, dava para olhar a captura e ver retransmissões, janelas e RTT. No QUIC, quase tudo isso está cifrado. As ferramentas mudaram:

  • qlog. Formato padronizado de log de eventos do QUIC, emitido pelas bibliotecas (quic-go, quiche, msquic e outras). O visualizador qvis mostra congestionamento, perdas e streams ao longo do tempo.
  • SSLKEYLOGFILE. Navegadores e curl gravam as chaves de sessão nesse arquivo se a variável estiver definida. O Wireshark usa essas chaves para decifrar a captura QUIC. Use só em ambiente de teste: quem tem esse arquivo lê todo o tráfego.
  • chrome://net-export. Grava um log detalhado da pilha de rede do Chrome, incluindo por que ele desistiu do QUIC para um destino.

Os custos

CPU. O TCP tem décadas de otimização no kernel e offload na placa de rede: segmentação, checksum e agregação de pacotes em hardware. O QUIC processa cada pacote em userspace e cifra cada um individualmente. GSO e GRO para UDP no Linux reduziram muito a diferença, mas servidores QUIC ainda gastam mais CPU por byte transferido.

Em Linux, bibliotecas QUIC frequentemente pedem buffers UDP maiores que o padrão do kernel. A quic-go, por exemplo, avisa quando não consegue aumentar o buffer e recomenda:

sysctl -w net.core.rmem_max=7500000
sysctl -w net.core.wmem_max=7500000

0-RTT e replay. Dados enviados no primeiro pacote de uma reconexão podem ser capturados e reenviados por um atacante. O servidor não tem como distinguir o original da cópia. Só aceite 0-RTT para requisições idempotentes, como GET sem efeito colateral. O RFC 8470 define o status 425 Too Early para o servidor recusar e pedir que o cliente repita depois do handshake.

Load balancers. Um balanceador que distribui UDP por hash de IP e porta quebra a migração de conexão: quando o IP do cliente muda, o pacote vai para outro servidor, que não conhece a conexão. Balanceadores cientes de QUIC roteiam pelo Connection ID. Confira o que o seu faz antes de colocar QUIC atrás dele.

Observabilidade. Ferramentas que mediam RTT e retransmissão passivamente, lendo cabeçalhos TCP, ficam cegas. O spin bit do QUIC permite medir RTT sem decifrar, mas é opcional e muitas implementações o desligam.

Quando HTTP/3 não faz diferença

O QUIC brilha em rede com perda e latência: celular, Wi-Fi, conexões intercontinentais, páginas com muitas requisições pequenas. Entre serviços no mesmo datacenter, com perda próxima de zero, o ganho some e o custo de CPU fica. Para tráfego interno entre microserviços, HTTP/2 sobre TCP continua sendo uma escolha perfeitamente razoável.

Checklist

  • Nginx 1.25+ com http_v3, ou servidor/CDN com HTTP/3 ativo.
  • listen ... quic e listen ... ssl lado a lado, em IPv4 e IPv6.
  • UDP/443 liberado no firewall do host, no security group e em qualquer appliance no caminho.
  • Alt-Svc enviado; opcionalmente, registro DNS HTTPS com alpn="h3,h2".
  • Teste com curl --http3-only de fora da sua rede.
  • 0-RTT desligado, ou restrito a requisições idempotentes.
  • Balanceador que entende Connection ID, ou consciência de que migração de conexão não vai funcionar.
  • Buffers UDP do kernel ajustados; CPU dos servidores de borda monitorada depois da ativação.

Em resumo

O transporte da web mudou de TCP para QUIC sem que a maioria percebesse, porque as CDNs fizeram a troca. Para quem opera a própria borda, ativar é simples. O trabalho está em garantir que o UDP chega, que o balanceador entende o protocolo, e que o monitoramento enxerga algo além de "UDP cifrado na 443". E, como tudo cai para TCP em silêncio, o teste tem que ser explícito.

Para o contexto de como a pilha TCP/IP chegou até aqui, veja a história da internet. Para a parte de criptografia do handshake, o texto sobre criptografia pós-quântica no TLS cobre o que muda quando o ClientHello cresce, e isso afeta o QUIC também.

Fontes

Adelson de Bittencourt Junior

Desenvolvedor full-stack em Criciúma, SC, com passagem por suporte de telefonia VoIP e redes. Escreve sobre redes, segurança, infraestrutura e bancos de dados. Sobre o site · Enviar correção