Um protocolo que cresceu à vista de todos
HTTP é a sigla de Hypertext Transfer Protocol: o protocolo de requisição e resposta que o cliente usa para pedir um recurso ao servidor, e que o servidor usa para responder. Ele nasceu junto com a própria web, por volta de 1991, e cada versão desde então foi uma resposta à mesma pressão: as páginas não paravam de crescer, os sites recebiam cada vez mais acessos, e o protocolo era cobrado para fazer coisas que a versão anterior deixava lentas. Cada versão é definida numa (Request for Comments), a série de documentos em que o (Internet Engineering Task Force) publica os padrões da internet. Este artigo indica a RFC em vigor de cada versão, porque em 2022 o conjunto todo foi reorganizado, e os números de RFC que a maioria das pessoas decorou não são mais os que valem.
HTTP/0.9 (1991): uma linha, uma resposta
O protocolo original era uma linha só: GET /pagina.html, enviada por uma conexão TCP (Transmission Control Protocol) recém-aberta. O servidor respondia com os bytes do documento e fechava a conexão. Não havia headers (cabeçalhos), códigos de status nem tipos de conteúdo, e o único método era o GET: a resposta só podia ser HTML, porque não havia como dizer que era outra coisa. Ninguém chamava aquilo de 0.9 na época; o número foi dado depois, quando passou a existir um esquema de versões de verdade, para dar um nome ao protocolo de uma linha. Vale conhecê-lo porque o esqueleto dele (um método, um caminho, uma resposta) continua visível em todas as versões que vieram depois.
HTTP/1.0 (1996): chegam os headers
O HTTP/1.0, documentado na RFC 1945 (maio de 1996), transformou a linha única num protocolo de verdade. As requisições ganharam número de versão e headers; as respostas ganharam uma linha de status ("200 OK", "404 Not Found") e headers próprios; o Content-Type permitiu que a resposta fosse uma imagem, uma folha de estilos ou qualquer outra coisa com um tipo (Multipurpose Internet Mail Extensions); e o POST e o HEAD se juntaram ao GET. O que ele não resolveu foi o modelo de conexão: uma requisição, uma conexão TCP, encerrada depois de cada resposta. Uma página com dez imagens exigia onze handshakes TCP (o aperto de mão que abre cada conexão), e o custo dessa cerimônia toda foi o que motivou a versão seguinte.
HTTP/1.1 (1997-1999): a versão que carregou a web por muito tempo
O HTTP/1.1 apareceu primeiro na RFC 2068 (janeiro de 1997) e foi refinado na RFC 2616 (junho de 1999), o documento que uma geração inteira de engenheiros cresceu citando. As conexões passaram a ser persistentes por padrão, e muitas requisições podiam reaproveitar uma mesma conexão TCP. O chunked transfer encoding (o envio da resposta em pedaços) permitiu que o servidor começasse a mandar uma resposta antes de saber o tamanho dela. O header Host, agora obrigatório, tornou possível a hospedagem virtual por nome: muitos sites num mesmo endereço IP, o modelo sobre o qual toda a indústria de hospedagem foi construída. O cache ganhou mecanismos de verdade: validadores, Cache-Control, requisições condicionais. O fracasso famoso dessa versão foi o pipelining, que permitia várias requisições em trânsito ao mesmo tempo, mas exigia que as respostas voltassem em ordem; assim, uma resposta lenta travava todas as que estavam na fila atrás dela. Foi o primeiro contato com o head-of-line blocking (o bloqueio de cabeça de fila), e deu tanto problema na prática que os navegadores deixaram o pipelining desligado. Em 2014 a especificação foi reescrita, para ficar mais precisa, nas RFCs 7230 a 7235, sem mudar o protocolo.
HTTP/2 (2015): binário e multiplexado
O , publicado como RFC 7540 (maio de 2015) e nascido do experimento SPDY do Google, manteve a semântica do HTTP (os mesmos métodos, códigos de status e headers) e trocou o formato de transmissão (wire format) que fica por baixo dela. As mensagens viraram frames (quadros) binários, transportados em streams (fluxos) numerados, e muitos streams são multiplexados numa única conexão TCP; assim, uma resposta lenta já não bloqueia as outras na camada do HTTP. Os headers, que se repetem muito de uma requisição para outra, são comprimidos com o HPACK (RFC 7541). A especificação também incluía o server push, que deixava o servidor enviar recursos antes de eles serem pedidos; isso nunca deu resultado na prática, e os navegadores acabaram abandonando o recurso. Um problema continuou fora de alcance, por causa da própria estrutura: todos esses streams ainda dividem uma conexão TCP, e o TCP entrega os bytes estritamente em ordem; então um único pacote perdido trava todos os streams até ser retransmitido. O HTTP/2 tirou o head-of-line blocking do HTTP e o deixou no transporte.
HTTP/3 (2022): um transporte novo
O , publicado como RFC 9114 (junho de 2022), resolve esse último problema trocando a base sobre a qual o HTTP roda. Ele coloca o HTTP sobre o QUIC (RFC 9000), um transporte sobre UDP (User Datagram Protocol) que já traz embutidos os streams, a recuperação de perdas e o handshake do TLS 1.3, e que está descrito ao lado dos seus parentes em TLS 1.2 vs TLS 1.3 vs DTLS vs QUIC. Como o QUIC recupera as perdas stream por stream, um pacote perdido trava só o próprio stream: o head-of-line blocking na camada de transporte, de que o HTTP/2 não conseguia escapar, deixa de existir. A compressão de headers passa do HPACK para o QPACK (RFC 9204), redesenhado para que o estado da compressão não volte a criar bloqueio entre streams. A criptografia não é opcional, porque o QUIC não tem modo sem criptografia. E, como o primeiro contato ainda costuma começar por TCP, os servidores anunciam o seu endpoint HTTP/3 pelo mecanismo Alt-Svc (RFC 7838) e, cada vez mais, por registros DNS do tipo HTTPS, e o navegador passa para o QUIC no acesso seguinte.
O problema que cada versão resolvia
Lidas em sequência, as versões são um único raciocínio sobre uma única restrição: toda requisição custa uma ida e volta na rede, e ida e volta é latência que nenhuma otimização elimina.
O HTTP/1.0 abria uma conexão TCP por requisição e a fechava depois da resposta. Uma página com quarenta imagens pagava quarenta aberturas de conexão, cada uma com o seu handshake e o seu slow start (a partida lenta do TCP). Os navegadores contornaram isso abrindo várias conexões ao mesmo tempo, o que passou o custo para o servidor e para a rede, em vez de eliminá-lo.
O HTTP/1.1 trouxe as conexões persistentes e o pipelining. O keep-alive acabou com a maior parte do desperdício. O pipelining (mandar várias requisições sem esperar cada resposta) foi especificado e depois abandonado na prática, porque as respostas tinham de voltar em ordem: uma resposta lenta travava tudo o que estava na fila atrás dela. Isso é o head-of-line blocking, e é o fio que liga as duas versões seguintes.
Ele também trouxe as peças que tornaram possível a web moderna: o header Host, para que muitos sites pudessem dividir um mesmo endereço, que foi o que permitiu a hospedagem virtual e, mais tarde, a economia da hospedagem com endereços compartilhados; o chunked transfer encoding; os validadores de cache; e os byte ranges (os pedidos de faixas de bytes).
O HTTP/2 tirou o bloqueio da camada de aplicação. Uma única conexão carrega muitos streams intercalados, divididos em frames binários, e não em texto. Os headers deixaram de ser retransmitidos por inteiro a cada requisição: o HPACK os comprime usando uma tabela compartilhada, o que faz uma diferença enorme quando uma página dispara centenas de requisições com headers quase idênticos. O server push entrou na especificação e, desde então, foi praticamente abandonado: ele errava o palpite com tanta frequência que desperdiçava mais banda do que economizava.
Mas o bloqueio mudou de lugar, em vez de sumir. O TCP garante a entrega em ordem, então um pacote perdido trava todos os streams daquela conexão enquanto é retransmitido. Numa rede limpa, o HTTP/2 é mais rápido; numa rede com perda de pacotes (rede móvel, links congestionados), ele pode ser mais lento que várias conexões HTTP/1.1.
O HTTP/3 resolve isso trocando o transporte. O QUIC roda sobre UDP e implementa os streams por conta própria, então um pacote perdido trava só o próprio stream. O handshake do transporte se funde com o handshake criptográfico, e identificadores de conexão substituem a tupla de endereços, de modo que a sessão sobrevive à troca do para a rede móvel.
Semântica, sintaxe e o que a reorganização de 2022 mudou
A revisão de 2022 separou nas especificações a semântica (métodos, códigos de status, campos de header, cache, requisições condicionais) da sintaxe de transmissão de cada versão. Isso não é mera arrumação: significa que um código de status ou um método é definido uma vez só e vale do mesmo jeito no 1.1, no 2 e no 3, e o que muda é só o framing.
A consequência prática é que trocar de versão muda o desempenho e a multiplexação, não o significado. Um 304 Not Modified se comporta do mesmo jeito em qualquer versão; o que muda é quantas idas e voltas foram necessárias para fazer a pergunta.
Duas coisas mudaram, sim, na transmissão, e pegam muita gente de surpresa. Os nomes dos campos de header são em minúsculas no HTTP/2 e no HTTP/3: um nome com maiúsculas é erro de protocolo, o que aparece em código que comparava nomes de headers ao pé da letra. E a linha de requisição foi substituída por pseudo-headers (:method, :scheme, :authority, :path), então as ferramentas que fazem o parsing de uma linha de requisição crua não têm mais o que ler.
A reorganização de 2022: a semântica de um lado, a transmissão do outro
A coisa mais útil para saber sobre as especificações atuais do HTTP é como elas estão organizadas hoje. Em junho de 2022, o HTTP Working Group do IETF separou o significado do protocolo da forma de transmiti-lo. A RFC 9110 define a semântica do HTTP (métodos, códigos de status, campos de header, negociação de conteúdo) uma vez só, para todas as versões; a RFC 9111 define o cache da mesma forma. Os formatos de transmissão ganham um documento cada: a RFC 9112 para a sintaxe em texto do HTTP/1.1, a RFC 9113 para o framing do HTTP/2 e a RFC 9114 para o HTTP/3 sobre QUIC. Juntas, elas tornam obsoletas todas as especificações anteriores do HTTP (a RFC 1945, a 2068, a 2616, a série 7230 e a 7540), então citar a RFC 2616 hoje é citar história. O working group mantém a lista oficial e atualizada em httpwg.org/specs, e o RFC Editor é a fonte canônica dos próprios documentos. Um GET continua sendo um GET em todas as versões desde a 0.9; o que mudou, cinco vezes, foi a forma como viajam os bytes que dizem isso.
A operação e o lugar de cada versão hoje
Vale conhecer a negociação de versão com precisão, porque é ela que explica o que se vê no tráfego. O HTTP/2 sobre TLS é escolhido por durante o handshake, então não há ida e volta extra. O HTTP/3 não pode ser negociado assim, porque é outro transporte; por isso o servidor o anuncia com o header Alt-Svc ou com um registro DNS HTTPS, e o cliente passa a usá-lo numa conexão seguinte. É por isso que a primeira visita de um navegador costuma ser em HTTP/2 e a segunda, em HTTP/3.
Onde cada uma ainda leva vantagem. O HTTP/1.1 continua sendo a resposta certa para APIs internas simples, health checks e tudo aquilo que alguém depura na mão com um cliente de texto. O HTTP/2 é o padrão para o tráfego dos navegadores até os servidores de origem, em redes bem-comportadas, e para muitos protocolos de comunicação entre serviços. O HTTP/3 compensa mais em redes com perda de pacotes e em redes móveis, e em conexões que migram.
O que quebra na prática, porque são esses os chamados que se repetem: middleboxes que bloqueiam UDP e forçam um fallback silencioso; load balancers que terminam o HTTP/2 e abrem conexões 1.1 até o backend, de modo que o ganho da multiplexação para na borda; headers hop-by-hop, do tipo Connection, que são proibidos no HTTP/2 e rejeitados, em vez de ignorados; e ferramentas de inspeção escritas para fazer o parsing de linhas de requisição.
A medição também muda. Contar conexões deixa de descrever a carga quando uma única conexão carrega centenas de requisições simultâneas; é a mesma razão pela qual o balanceamento por least connections perde o sentido.
Implementações, por categoria
- Servidores e proxies: NGINX, Apache, Caddy, HAProxy, Envoy, Traefik. O HTTP/2 é universal; o suporte ao HTTP/3 chegou depois e de forma desigual, e vale confirmar versão por versão, e não produto por produto.
- Redes de distribuição de conteúdo: Cloudflare, Fastly, Akamai e similares. Na prática, são elas que mais implantam HTTP/3, e muitas vezes é por elas que um site passa a ter o protocolo sem mudar nada na origem.
- Bibliotecas cliente: o curl e as stacks nativas de cada linguagem. O suporte ao HTTP/3 varia tanto que a lista de versões de um cliente é uma questão real de compatibilidade, e não uma nota de rodapé.
- Navegadores: são o motivo de o HTTP/2 e o HTTP/3 terem sido implantados, e o motivo de o server push ter morrido: são eles que definem as configurações padrão que decidem qual versão é de fato usada.