---
title: "QUIC e HTTP/3 na prática: como ativar, testar e depurar a web sobre UDP"
description: "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."
date: "2026-09-29"
tags: ["quic", "http3", "udp", "tcp", "tls", "nginx", "redes", "web"]
url: "https://abjr.dev/noticias/quic-http3-udp-transporte-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:

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

Configuração mínima:

```nginx
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:

```bash
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?**

```bash
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:

```bash
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](/noticias/historia-da-internet). Para a parte de criptografia do handshake, o texto sobre [criptografia pós-quântica no TLS](/noticias/criptografia-pos-quantica-ml-kem-tls) cobre o que muda quando o ClientHello cresce, e isso afeta o QUIC também.

## Fontes

- [RFC 9000 — QUIC](https://www.rfc-editor.org/rfc/rfc9000)
- [RFC 9001 — Using TLS to Secure QUIC](https://www.rfc-editor.org/rfc/rfc9001)
- [RFC 9002 — QUIC Loss Detection and Congestion Control](https://www.rfc-editor.org/rfc/rfc9002)
- [RFC 9114 — HTTP/3](https://www.rfc-editor.org/rfc/rfc9114)
- [RFC 9204 — QPACK](https://www.rfc-editor.org/rfc/rfc9204)
- [RFC 9460 — SVCB e HTTPS DNS records](https://www.rfc-editor.org/rfc/rfc9460)
- [RFC 8470 — Using Early Data in HTTP (425 Too Early)](https://www.rfc-editor.org/rfc/rfc8470)
- [Nginx — Support for QUIC and HTTP/3](https://nginx.org/en/docs/quic.html)
- [curl — HTTP/3](https://curl.se/docs/http3.html)
- [quic-go — UDP buffer sizes](https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes)
- [qvis — visualizador de qlog](https://qvis.quictools.info/)
- [Cloudflare Radar — Adoption and Usage](https://radar.cloudflare.com/adoption-and-usage)
