Cinco classes e a história por trás delas
O primeiro dígito do código entrega a categoria, e essa leitura já diz de que lado procurar o problema. É a triagem que um bom dev faz em meio segundo antes de abrir qualquer log. A semântica de cada classe está fixada na RFC 9110 (HTTP Semantics), o documento que hoje serve de referência para HTTP/1.1, HTTP/2 e HTTP/3 ao mesmo tempo.
| Classe | Significado | De quem é o problema |
|---|---|---|
| 1xx | Informativo: resposta provisória, o pedido continua. | De ninguém, é um aviso intermediário. |
| 2xx | Sucesso: o pedido foi recebido, entendido e aceito. | De ninguém, deu certo. |
| 3xx | Redirecionamento: falta uma ação para concluir (ir a outra URL, usar o cache). | Compartilhado: siga a instrução. |
| 4xx | Erro do cliente: sintaxe, autenticação, permissão ou recurso. | Seu pedido, conserte a requisição. |
| 5xx | Erro do servidor: falhou ao processar um pedido aparentemente válido. | O servidor, o outro lado caiu. |
Essa divisão parece óbvia hoje, mas levou décadas para se estabilizar. A numeração nasceu enxuta e foi ganhando códigos à medida que a web descobria novos problemas, de portais de Wi-Fi a rate limiting. Vale conhecer a linha do tempo porque ela explica por que existem pares quase idênticos como 302 e 307: eles são de eras diferentes, criados para corrigir ambiguidades da geração anterior.
- Maio de 1996RFC 1945, HTTP/1.0
A primeira versão documentada. Introduz a linha de status e um punhado de códigos (200, 301, 302, 400, 404, 500).
- Janeiro de 1997RFC 2068, HTTP/1.1
O primeiro recorte padronizado do HTTP/1.1, com conexões persistentes e mais códigos.
- Junho de 1999RFC 2616, HTTP/1.1 revisada
A revisão que foi a referência por quinze anos, num único documento monolítico.
- Junho de 2014RFC 7230–7235
A 2616 é quebrada em seis documentos (sintaxe, semântica, condicionais, range, cache, autenticação).
- Junho de 2022RFC 9110 / 9111 / 9112
A referência atual: semântica (9110), cache (9111) e HTTP/1.1 (9112). Obsoleta a série 723x e vale para todas as versões.
Redirecionar sem quebrar o método: 301 vs 308, 302 vs 307
O bloco 2xx é o mais tranquilo: 200 OK é o sucesso com corpo, 201 Created confirma que um POST criou um recurso (e costuma vir com o header Location) e 204 No Content é o sucesso mudo, típico de um DELETE. A armadilha de verdade está no 3xx, onde uma decisão sutil separa um redirect inofensivo de um bug que só aparece em produção: o código muda o método HTTP?
Aqui está o detalhe que a RFC 9110 registra em nota: por razões históricas, ao seguir um 301 (§15.4.2) ou um 302 (§15.4.3) o cliente PODE trocar o método de POST para GET. Os navegadores fazem isso há décadas, e a spec apenas descreve o comportamento real. Já o 307 Temporary Redirect (§15.4.8) e o 308 Permanent Redirect (§15.4.9) foram criados justamente para NÃO permitir essa troca: eles preservam o método e o corpo. O eixo permanente/temporário é ortogonal a isso, 301 e 308 são permanentes; 302 e 307 são temporários.
| Código | Duração | Método preservado? | Cacheável por padrão? | O que o crawler faz |
|---|---|---|---|---|
| 301 Moved Permanently | Permanente | Não, pode trocar POST→GET | Sim | Consolida os sinais na nova URL |
| 308 Permanent Redirect | Permanente | Sim, mantém método e corpo | Sim | Consolida os sinais na nova URL |
| 302 Found | Temporário | Não, pode trocar POST→GET | Não | Mantém a URL original indexada |
| 307 Temporary Redirect | Temporário | Sim, mantém método e corpo | Não | Mantém a URL original indexada |
Exemplo trabalhado nº 1: um checkout envia um POST com o valor da compra. Se o servidor responde 302, o navegador segue o redirect trocando o método por GET e DESCARTANDO o corpo, o pagamento se perde no caminho e o endpoint de confirmação recebe um GET vazio. Com 307, o cliente repete o POST com o mesmo corpo. Veja a diferença byte a byte:
# Envio um formulário de pagamento com POST
POST /checkout HTTP/1.1
Host: loja.com
Content-Type: application/x-www-form-urlencoded
valor=199.90&metodo=cartao
# (A) Servidor responde 302 -> o navegador troca POST por GET
HTTP/1.1 302 Found
Location: /obrigado
GET /obrigado HTTP/1.1 <- metodo virou GET, corpo descartado
Host: loja.com
# (B) Mesmo fluxo com 307 -> o cliente REPETE metodo e corpo
HTTP/1.1 307 Temporary Redirect
Location: /obrigado
POST /obrigado HTTP/1.1 <- continua POST, corpo preservado
Host: loja.com
Content-Type: application/x-www-form-urlencoded
valor=199.90&metodo=cartaoA regra prática cai sozinha: para mover em definitivo uma página comum (GET) e preservar o ranking, 301 é o clássico e o Google trata 301 e 308 como equivalentes na consolidação de sinais. Para mover em definitivo um endpoint que recebe POST, use 308. Para um desvio passageiro, 302 basta, a menos que o método importe, e aí é 307. Consulte o significado, a RFC e a cacheabilidade de qualquer código na ferramenta abaixo antes de escolher.
4xx: o pedido é seu, 401 vs 403, 404 vs 410
O 4xx aponta para o que você mandou. Alguns são diretos: 400 Bad Request é pedido malformado, 405 Method Not Allowed é o verbo errado (e o servidor deve listar os aceitos no header Allow), 409 Conflict é choque de estado. Mas dois pares fazem gente perder horas procurando bug no lugar errado: 401 contra 403 e 404 contra 410. A diferença entre eles não é de gravidade, é de significado.
401 Unauthorized
- “Não sei quem você é”: falta autenticação válida (token ausente, expirado ou inválido).
- Reautenticar, logar de novo, renovar o token, pode resolver.
- A RFC 9110 §15.5.2 EXIGE o header WWW-Authenticate com um desafio.
403 Forbidden
- “Sei quem você é e você não pode”: falta permissão (papel, escopo, IP, regra de WAF).
- Reautenticar não ajuda, o problema é autorização, não identidade.
- O servidor pode explicar o motivo ou, para esconder a existência do recurso, responder 404 no lugar.
O outro par vive na fronteira entre backend e SEO. 404 Not Found (§15.5.5) diz “não existe nessa URL”, pode ser rota errada, recurso removido ou até o servidor escondendo que o recurso existe. 410 Gone (§15.5.11) é mais assertivo: “existiu e foi removido de propósito”. A pergunta que importa não é técnica, é o que cada um sinaliza ao Googlebot.
| Aspecto | 404 Not Found | 410 Gone |
|---|---|---|
| Significado | Não existe nessa URL (ou o servidor não revela). | Existiu e foi removido em definitivo. |
| Intenção | Pode ser erro de rota ou algo passageiro. | Declaração explícita de “não volta”. |
| Cacheável por padrão | Sim | Sim |
| O que o Googlebot faz | Remove do índice com o tempo. | Remove do índice com o tempo, o Google diz tratar igual a 404. |
304 Not Modified: a conversa que economiza banda
O 304 Not Modified (§15.4.5) é o único 3xx que não redireciona nada, ele é o motor da revalidação de cache. A ideia: em vez de baixar de novo um recurso que talvez não tenha mudado, o cliente pergunta “mudou?” e, se a resposta for não, reaproveita o que já tem. Essa negociação usa validadores. O servidor manda um ETag (uma impressão digital do conteúdo, RFC 9110 §8.8.3) ou um Last-Modified (§8.8.2). Na próxima visita, o cliente devolve esses valores nos headers condicionais If-None-Match e If-Modified-Since (requisições condicionais, RFC 9110 §13; regras de cache, RFC 9111).
# Primeira visita: o servidor devolve o recurso + um validador
GET /static/app.css HTTP/1.1
Host: exemplo.com
HTTP/1.1 200 OK
Content-Type: text/css
Content-Length: 46080
Cache-Control: max-age=0, must-revalidate
ETag: "9f8a1c-b3d0"
Last-Modified: Tue, 07 Jul 2026 10:00:00 GMT
...46080 bytes de CSS...
# Recarga: o cliente pergunta "mudou?" com o validador que guardou
GET /static/app.css HTTP/1.1
Host: exemplo.com
If-None-Match: "9f8a1c-b3d0"
If-Modified-Since: Tue, 07 Jul 2026 10:00:00 GMT
# Nada mudou -> resposta sem corpo
HTTP/1.1 304 Not Modified
ETag: "9f8a1c-b3d0"
Cache-Control: max-age=0, must-revalidate
Date: Wed, 08 Jul 2026 09:00:00 GMT
(sem corpo / no body)Exemplo trabalhado nº 2: suponha um arquivo CSS de 45 KB, ou seja, 46.080 bytes (45 × 1.024). Na primeira visita, o cliente baixa os 46.080 bytes e guarda o ETag "9f8a1c-b3d0". Na recarga, ele envia If-None-Match com esse valor. Se nada mudou, o servidor responde 304 devolvendo só os cabeçalhos, cerca de 180 bytes, sem corpo. Em vez de 46.080 bytes, trafegam 180: uma economia de 45.900 bytes (46.080 − 180), ou 99,6% (45.900 ÷ 46.080). Multiplique isso por dezenas de recursos por página e milhares de visitas, e o 304 vira uma das maiores alavancas de performance da web.
Ver os dados
| Categoria | Valor |
|---|---|
| 200 OK (corpo completo) | 46.080 bytes |
| 304 Not Modified (revalidação) | 180 bytes |
Dois detalhes fecham o assunto. Primeiro, um 304 nunca carrega corpo, se um proxy ou framework devolve conteúdo num 304, algo está errado. Segundo, ETags vêm em duas formas: forte (o conteúdo é idêntico byte a byte) e fraca (prefixo W/, o conteúdo é equivalente o suficiente). A fraca é útil quando compressão ou espaços em branco variam sem mudar o significado. Em ambos os casos, é o servidor que decide se o validador ainda vale.
5xx: o outro lado falhou, 500 vs 503 e o 429 no meio
No 5xx o problema é do servidor, mas reconhecer qual código apareceu direciona o debug. O 500 Internal Server Error (§15.6.1) é o genérico, quase sempre uma exceção não tratada; a resposta útil está no log, não no número. O 502 Bad Gateway e o 504 Gateway Timeout envolvem um proxy: o 502 é resposta inválida do serviço de trás (a app caiu), o 504 é o proxy sem resposta a tempo (a app travou). E o 503 Service Unavailable (§15.6.4) é o mais honesto da família.
| Código | O que costuma ser | Temporário? |
|---|---|---|
| 500 Internal Server Error | Exceção não tratada; bug na aplicação. | Não diz nada sobre retry |
| 502 Bad Gateway | Resposta inválida do upstream (app caiu). | Talvez |
| 503 Service Unavailable | Indisponibilidade momentânea (deploy, sobrecarga). | Sim, aceita Retry-After |
| 504 Gateway Timeout | O upstream demorou demais (query travada). | Talvez |
A diferença prática entre 503 e 500 é o que o cliente deve fazer. O 500 não promete nada, pode ser um bug permanente. O 503 diz “estou fora agora, volte depois” e pode anexar o header Retry-After (RFC 9110 §10.2.3) indicando quando tentar de novo. Esse mesmo header aparece no primo do 4xx que sinaliza excesso: o 429 Too Many Requests (RFC 6585 §4). Ao estourar um rate limit, um servidor bem-educado responde 429 e sugere no Retry-After quanto esperar; um cliente bem-educado obedece antes de tentar de novo.
Escolhendo o código certo (e os cantos curiosos)
Escolher o status certo é uma decisão de projeto, não um detalhe de última hora. O código é a primeira coisa que outro sistema lê, um proxy, um crawler, um cliente que decide se tenta de novo. Errá-lo é mentir para quem confia nessa leitura. Este checklist resolve a maioria dos casos:
- Movi uma página comum (GET) em definitivo? 301. É um endpoint de API que recebe POST? 308 (preserva o método).
- Desvio passageiro? 302. Se o método precisa sobreviver, 307.
- Falta login/token? 401, e nunca esqueça o header WWW-Authenticate. Logado mas barrado? 403.
- Página que não existe? 404. Removida de propósito? 410. Nunca 200 com “não encontrado” no corpo.
- Estourou o rate limit? 429 com Retry-After. Fora do ar por deploy/sobrecarga? 503 com Retry-After.
- Exceção inesperada no backend? 500, e vá direto ao log. Recurso em cache pode continuar válido? Emita ETag e responda 304 na revalidação.
Por que 307 e 308 nasceram
A intenção original de 301 e 302 era que o cliente repetisse o mesmo método na nova URL. Só que os navegadores, por interoperabilidade, passaram a trocar POST por GET ao seguir esses redirects, e a prática virou regra de fato. A RFC 9110 apenas registra essa nota histórica. Como não dava para “consertar” 301 e 302 sem quebrar a web inteira, criaram-se dois códigos novos e inequívocos: 307 (temporário) e 308 (permanente), ambos com uma única promessa, não mexer no método nem no corpo.
O soft 404, em detalhe
Um soft 404 não é um código, é um diagnóstico do Google para uma resposta 200 cujo conteúdo indica “não encontrado” ou está vazio. Ele custa caro por dois motivos: gasta orçamento de rastreamento em URLs que deveriam sumir e polui o índice com páginas sem valor. A correção é simples e fica no servidor, não no HTML: faça a rota inexistente devolver 404 (ou 410) na linha de status. Framework que renderiza uma bonita tela de erro com HTTP 200 é a causa número um desse problema.
418 I’m a teapot: a piada que virou reservada
O 418 vem da RFC 2324, o Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0), publicada em 1º de abril de 1998, uma brincadeira de Dia da Mentira. A regra: qualquer tentativa de fazer café num bule (teapot) deve resultar em “418 I’m a teapot”. A RFC 7168 (HTCPCP-TEA, 2014) até estendeu a piada para chá. Nada disso é padrão sério, mas o registro oficial da IANA marca o 418 como (Unused) e a RFC 9110 §15.5.19 o reserva, justamente para que ninguém o reaproveite e quebre a piada. Na prática, alguns serviços o usam como easter egg ou resposta de bloqueio.
429 e o backoff exponencial
Quando recebe um 429, a primeira regra é respeitar o Retry-After, se ele vier. Se não vier, não fique martelando o servidor: recue de forma exponencial, espere 1 s, depois 2 s, 4 s, 8 s, e some um pouco de aleatoriedade (jitter) para que muitos clientes que caíram juntos não voltem todos no mesmo instante e derrubem o serviço de novo. Um teto máximo de espera evita esperas absurdas. É a diferença entre um cliente que ajuda o servidor a se recuperar e um que participa do afogamento.
Ler o status é só o começo; os cabeçalhos ao redor completam a história. Depois de acertar o código, vale auditar o resto da resposta: o analisador de headers de segurança dá uma nota aos seus headers, o inspetor de cookies destrincha as flags de Set-Cookie, e o verificador de links checa a segurança de uma URL antes de você abri-la. Juntos, eles transformam “que número foi esse?” em “o que a resposta inteira está dizendo?”.
Perguntas frequentes
301 ou 302 para trocar a URL de uma página?
Quando usar 307/308 em vez de 302/301?
Qual a diferença entre 401 e 403?
404 ou 410 para uma página removida? O Google trata diferente?
O que é um soft 404 e por que é um problema?
O que fazer ao receber um 429?
O 418 “I’m a teapot” é um código real?
Cada código HTTP é uma decisão. Leia o primeiro dígito para saber de que lado está o problema; escolha entre 301/308 e 302/307 pela pergunta “o método precisa sobreviver?”; use 401 (com WWW-Authenticate) para identidade e 403 para permissão; devolva 404/410 de verdade e nunca um 200 disfarçado; e deixe o 304 com ETag economizar banda. Com a RFC 9110 como referência, o status certo deixa de ser decoreba e vira parte do projeto.
Fontes e referências
- RFC 9110, HTTP Semantics (status codes, Retry-After, WWW-Authenticate)
- RFC 9111, HTTP Caching (validação, ETag, 304)
- RFC 6585, Additional HTTP Status Codes (428, 429, 431, 511)
- RFC 7538, 308 Permanent Redirect
- RFC 2324, HTCPCP/1.0 (a origem do 418)
- IANA, Registro oficial de códigos de status HTTP
- Google Search Central, Códigos de status HTTP e soft 404