Pular para o conteúdo
Novo

DeepSeek V4.1 Flash grátis por 7 dias: 10M tokens por dia após qualquer recarga. Até 30/11.

Como funciona

Como o HTTP funciona: requisições, respostas, cabeçalhos e códigos de status

Leia uma troca HTTP real linha por linha: métodos, URLs, cabeçalhos, classes de status e cookies, testados com curl em um servidor local.

Iniciante11 min de leituraPor NotHoly

O que é HTTP

HTTP é o protocolo que navegadores, apps e APIs usam para trocar mensagens. A RFC 9110 define o que essas mensagens significam: métodos, códigos de status, campos de cabeçalho e assim por diante. A RFC 9112 define como uma mensagem HTTP/1.1 se parece em texto puro sobre uma conexão TCP. A visão geral da MDN é uma introdução mais curta, voltada ao navegador, e funciona bem ao lado das RFCs.

O padrão é simples. Um cliente envia uma requisição, e um servidor devolve uma resposta. Quem sempre começa a troca é o cliente. HTTP puro não criptografa nada e não lembra quem você é entre uma requisição e outra. A criptografia vem do TLS, que o próximo artigo explica. A memória entre requisições vem de cookies e tokens, que veremos mais adiante.

Por que isso importa

Quase todo comportamento web que você vai estudar passa pelo HTTP. Um formulário envia um valor no corpo, uma API confia em um cabeçalho, um redirecionamento leva o usuário para outro lugar e um cache guarda a cópia errada de uma página. Quando você usar depois um proxy como o OWASP ZAP para inspecionar tráfego, ele vai mostrar exatamente essas mensagens. Se você não consegue ler uma requisição crua, a saída do proxy vira ruído.

A forma de uma mensagem

Uma requisição HTTP/1.1 é texto puro nesta forma:

http
GET / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/8.12.1
Accept: */*

A primeira linha é a linha de requisição: o método, o alvo da requisição e a versão do protocolo. Cada linha seguinte é um campo de cabeçalho, escrito como nome: valor. Uma linha em branco encerra a seção de cabeçalhos. Se a mensagem tiver corpo, ele vem depois dessa linha em branco. No fio, cada linha termina com CRLF (um retorno de carro seguido de uma quebra de linha), que o editor esconde de você.

Uma resposta segue o mesmo layout, com uma linha de status no topo: versão, código de status e frase de motivo.

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 15

<h1>hello</h1>

Métodos

O método diz o que o cliente quer que seja feito. A RFC 9110 define GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS e TRACE. O PATCH vem da RFC 5789.

Duas propriedades importam quando você lê tráfego:

  • Métodos seguros (GET, HEAD, OPTIONS, TRACE) devem ser somente leitura do ponto de vista do cliente. RFC 9110, seção 9.2.1.
  • Métodos idempotentes (GET, HEAD, PUT, DELETE, OPTIONS, TRACE) podem ser repetidos com o mesmo efeito pretendido de uma única requisição. RFC 9110, seção 9.2.2. O POST não é seguro nem idempotente.

Essas propriedades são promessas sobre significado. Quem decide o que realmente acontece é o servidor. Um handler de GET que apaga um registro quebra esse contrato, e um revisor de segurança vai procurar exatamente esse erro.

URLs e o alvo da requisição

Pegue a URL http://127.0.0.1:8000/missing?lang=en#top e separe as partes:

  • Esquema: http, que diz ao cliente qual protocolo usar.
  • Autoridade: 127.0.0.1:8000, o host e a porta opcional.
  • Caminho: /missing.
  • Query: lang=en, depois do ?.
  • Fragmento: top, depois do #.

O cliente não envia o esquema nem o fragmento ao servidor. A linha de requisição leva o caminho e a query. A autoridade vai no cabeçalho Host. O HTTP/1.1 exige Host porque muitos sites podem compartilhar um mesmo IP, e o servidor usa esse cabeçalho para decidir qual site deve responder.

Cabeçalhos

Nomes de cabeçalho não diferenciam maiúsculas de minúsculas, então Content-Type e content-type são o mesmo campo (RFC 9110, seção 5.1). Os valores são outra história: muitos diferenciam maiúsculas, então não presuma que não diferenciam.

Cabeçalhos de requisição que você vai ver com mais frequência:

  • Host: obrigatório em toda requisição HTTP/1.1. A RFC 9112, seção 3.2, diz que o servidor deve responder 400 quando ele faltar.
  • User-Agent: o software do cliente.
  • Accept: os tipos de mídia que o cliente consegue tratar.
  • Cookie: os cookies que o navegador devolve ao servidor.
  • Authorization: credenciais, muitas vezes um token bearer.
  • Content-Type e Content-Length: descrevem o corpo, quando há um.

Cabeçalhos de resposta que você vai ver com mais frequência:

  • Content-Type: o tipo de mídia do corpo, como text/html.
  • Content-Length: o tamanho do corpo em bytes.
  • Date: o relógio do servidor, em GMT.
  • Server: o nome do software. Muitos servidores o exibem. Em produção, costuma-se ocultá-lo.
  • Last-Modified: quando o servidor alterou o recurso pela última vez.
  • Set-Cookie: pede ao navegador que guarde um cookie.

Cabeçalhos são dados que quem envia controla. O navegador envia o que o software dele envia, e um script ou um proxy pode enviar o que quiser. O X-Forwarded-For é o exemplo clássico: um proxy o adiciona, mas um cliente também pode enviá-lo. Nunca confie em um cabeçalho só porque ele parece oficial.

Códigos de status

Um código de status tem três dígitos. O primeiro dígito indica a classe, definida na RFC 9110, seção 15:

Classe Significado Exemplos
1xx Informativo. A requisição foi recebida e o processamento continua 100 Continue
2xx Sucesso 200 OK, 201 Created, 204 No Content
3xx Redirecionamento. O cliente precisa dar outro passo 301 Moved Permanently, 304 Not Modified
4xx Erro do cliente. A requisição tem um problema 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed
5xx Erro do servidor. O servidor não conseguiu atender a uma requisição válida 500, 501 Not Implemented, 502, 503

Mesmo quando o cliente não reconhece um código específico, ele consegue agir pela classe. Um 4xx indica que a requisição precisa mudar. Um 5xx indica que a mesma requisição pode funcionar depois.

Dois códigos são confundidos com frequência. Um 401 significa que a requisição não tem credenciais válidas. Um 403 significa que o servidor entendeu a requisição e a recusa, mesmo com credenciais.

A frase de motivo, como Not Found, é um texto escolhido pelo servidor. A RFC 9112, seção 4, diz que o cliente deve ignorá-la. Escreva a lógica em cima do número, nunca das palavras.

Cookies

Cookies não fazem parte da RFC 9110. Eles são definidos na RFC 6265. O ciclo funciona assim:

  1. O servidor envia Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax.
  2. O navegador guarda o valor junto com os atributos.
  3. Em requisições seguintes para um site compatível, o navegador envia Cookie: session=abc123.

Um cookie de sessão é um identificador que o servidor entrega. Ele não é uma senha, e não é privado do usuário. Quem tem o valor normalmente consegue agir como aquela sessão até ela expirar ou ser revogada. HttpOnly impede que scripts da página leiam o valor. Secure permite enviá-lo só por HTTPS. SameSite limita quando o navegador o envia em requisições entre sites. Os artigos de segurança web voltam a esses atributos.

Laboratório prático

Você precisa de curl e Python 3. O Git Bash no Windows 11 inclui o curl, e a maioria dos sistemas Linux e macOS também tem. O servidor é o http.server, que vem com o Python. Rode-o apenas na sua máquina, preso a 127.0.0.1, e nunca o exponha a uma rede.

A saída abaixo vem de executar estes comandos no Windows 11 com Git Bash, curl 8.12.1 e Python 3.12.10. Nessa máquina, o python3 não estava no PATH do Git Bash, então o servidor foi iniciado com o caminho completo do executável Python 3.12. Use python3, py -3 ou python, conforme o seu sistema. Suas datas, horários e versões vão ser diferentes. A barra de progresso do curl foi removida da saída abaixo, e corpos HTML longos foram encurtados.

Passo 1: criar o site e iniciar o servidor

No primeiro terminal:

bash
mkdir httplab && cd httplab && echo '<h1>hello</h1>' > index.html
python3 -m http.server 8000 --bind 127.0.0.1

Deixe esse terminal aberto. O servidor escreve uma linha de log por requisição, então cada requisição que você enviar abaixo vai aparecer aqui.

Passo 2: ler uma troca completa

No segundo terminal:

bash
curl -v http://127.0.0.1:8000/

Saída:

text
*   Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.12.1
> Accept: */*
> 
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Date: Sun, 08 Nov 2026 08:16:48 GMT
< Content-type: text/html
< Content-Length: 15
< Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
< 
<h1>hello</h1>
* shutting down connection #0

Leia nesta ordem:

  • Linhas que começam com * são anotações do curl sobre a conexão.
  • Linhas que começam com > são o que o curl enviou. O próprio curl acrescentou Host, User-Agent e Accept.
  • Linhas que começam com < são o que o servidor devolveu.
  • A linha > vazia encerra os cabeçalhos da requisição. A linha < vazia encerra os cabeçalhos da resposta.
  • <h1>hello</h1> é o corpo. Content-Length: 15 bate com o arquivo, porque o echo acrescentou uma quebra de linha depois da tag.
  • HTTP 1.0, assume close after body mostra um descompasso. O curl pediu HTTP/1.1, mas o servidor respondeu em HTTP/1.0. O servidor simples do Python fala HTTP/1.0, então o curl fecha a conexão assim que lê o corpo.

Passo 3: pedir um arquivo que não existe

bash
curl -i http://127.0.0.1:8000/missing

A opção -i mostra os cabeçalhos da resposta junto com o corpo. Saída, com o corpo HTML encurtado:

text
HTTP/1.0 404 File not found
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:49 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 335

<!DOCTYPE HTML>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Error response</title>
    </head>
    <body>
        <h1>Error response</h1>
        <p>Error code: 404</p>
        <p>Message: File not found.</p>
        <p>Error code explanation: 404 - Nothing matches the given URI.</p>
    </body>
</html>

Olhe o primeiro terminal. Ele registrou:

text
127.0.0.1 - - [08/Nov/2026 05:16:49] code 404, message File not found
127.0.0.1 - - [08/Nov/2026 05:16:49] "GET /missing HTTP/1.1" 404 -

O código de status é 404, um código da classe de erro do cliente: o caminho não corresponde a nenhum recurso. A frase de motivo File not found é o texto escolhido pelo próprio servidor. O corpo é uma página HTML que o servidor gerou para o erro. Qualquer status pode trazer qualquer corpo, então leia os dois.

O log mostra 05:16 e o cabeçalho Date mostra 08:16. O log usa o horário local e o cabeçalho usa GMT, então os dois diferem pelo deslocamento do fuso local.

Passo 4: enviar um método que o servidor não implementa

bash
curl -i -X POST http://127.0.0.1:8000/

Saída, com o corpo HTML encurtado:

text
HTTP/1.0 501 Unsupported method ('POST')
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:49 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 357

<!DOCTYPE HTML>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Error response</title>
    </head>
    <body>
        <h1>Error response</h1>
        <p>Error code: 501</p>
        <p>Message: Unsupported method ('POST').</p>
        <p>Error code explanation: 501 - Server does not support this operation.</p>
    </body>
</html>

A linha de log diz code 501, message Unsupported method ('POST'). A opção -X muda só o método. O curl enviou os mesmos cabeçalhos de antes.

O servidor simples não tem nenhum handler de POST, então 501 Not Implemented é a resposta honesta. A RFC 9110, seção 15.6.2, define o 501 para um servidor que não oferece a funcionalidade de que a requisição precisa. Uma aplicação real que conhece o recurso, mas recusa POST nele, normalmente responderia 405 Method Not Allowed. Uma resposta 405 precisa incluir um cabeçalho Allow com os métodos que o recurso aceita.

Passo 5: pedir só os cabeçalhos com HEAD

bash
curl -I http://127.0.0.1:8000/index.html

Saída:

text
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:50 GMT
Content-type: text/html
Content-Length: 15
Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT

A opção -I envia uma requisição HEAD. O status e os cabeçalhos são iguais aos de um GET, mas nenhum corpo vem depois. O Content-Length continua dizendo 15, porque descreve a representação que um GET devolveria. A RFC 9110, seção 9.3.2, define esse comportamento. O log mostra "HEAD /index.html HTTP/1.1" 200 -.

O HEAD é útil quando você quer saber se um arquivo existe, qual é o tamanho dele ou quando foi alterado pela última vez, sem baixá-lo.

bash
curl -v -H 'Cookie: session=demo123' http://127.0.0.1:8000/

As linhas relevantes da saída:

text
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.12.1
> Accept: */*
> Cookie: session=demo123
> 
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Date: Sun, 08 Nov 2026 08:18:28 GMT
< Content-type: text/html
< Content-Length: 15
< Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
< 

A linha do corpo foi omitida aqui. O servidor respondeu com o mesmo 200 de antes, porque não tem lógica de cookies e ignora o cabeçalho. Esse é o ponto do exercício. O navegador envia cookies, mas quem decide o que eles significam é o servidor. Um servidor que nunca define um cookie não vai agir sobre um que você enviar.

Passo 7: parar o servidor

Pressione Ctrl+C no primeiro terminal quando terminar.

Erros comuns

  • Ler 200 como sucesso. Um 200 pode trazer uma página de erro. Confira o corpo e o que a aplicação fez.
  • Tratar GET como inofensivo. Seguro descreve a intenção do cliente. Se um GET altera dados, o servidor está errado, e atacantes vão tentar isso.
  • Escrever lógica em cima da frase de motivo. O cliente deve usar o número. O servidor pode mudar as palavras à vontade.
  • Confiar nos cabeçalhos do cliente. Host, User-Agent, Cookie e X-Forwarded-For vêm todos do lado do cliente. Valide-os no servidor.
  • Presumir que HTTP é privado. HTTP puro pode ser lido por qualquer pessoa no caminho entre você e o servidor. Por isso os sites reais usam HTTPS. O laboratório só é aceitável porque o tráfego nunca sai de 127.0.0.1.
  • Esperar que -X mude os cabeçalhos. Ele muda só o método. Para adicionar cabeçalhos, use -H.

Como seguir em frente

  • Leia a RFC 9110, seção 9 (métodos) e seção 15 (códigos de status). É longa, então leia por partes.
  • Leia a RFC 9112 para entender o enquadramento das mensagens: como cliente e servidor combinam onde o corpo termina, incluindo Content-Length e Transfer-Encoding.
  • Leia a visão geral de HTTP da MDN para ver os mesmos conceitos pelo lado do navegador.
  • Depois, o que é HTTPS e TLS mostra a mesma troca com criptografia. Se ainda não leu, o que é uma porta explica a parte :8000 da URL.

Um proxy como o ZAP exibe essas mesmas mensagens, e é por isso que este artigo dá base para os próximos, sobre vulnerabilidades web. A API da NotHoly, em api.notholy.com/v1, também é uma API HTTP com corpos JSON, servida sobre TLS, então os cabeçalhos e códigos de status acima valem quando você a chama.

Fontes

  1. RFC 9110: HTTP Semantics
  2. RFC 9112: HTTP/1.1
  3. MDN Web Docs: An overview of HTTP
  4. RFC 6265: HTTP State Management Mechanism (cookies)

Mais em Redes e web

Redes e web

O que é TLS? Como o HTTPS protege o tráfego

TLS 1.3 dá ao HTTPS confidencialidade, integridade e autenticação do servidor. Veja o handshake, as cadeias de certificados e o que o cadeado não prova.

Iniciante12 min de leitura