Redes

Códigos de status HTTP: as decisões que cada número carrega

Todo pedido HTTP volta com um número de três dígitos, e a MDN já lista todos eles melhor do que qualquer artigo faria. O que ninguém organiza é o que interessa na hora do bug: cada número é uma decisão de engenharia com consequências reais. Devolver 302 onde deveria ser 307 quebra um POST em produção. Devolver 200 numa página que não existe engana o Googlebot e afunda o SEO. Esquecer o header WWW-Authenticate num 401 viola a própria RFC. Este guia percorre os pares que todo mundo confunde e mostra a escolha que cada código carrega, com a RFC 9110 (que em junho de 2022 substituiu a série 7230–7235) na mão o tempo todo. Use a [referência de status HTTP](tool:http-status-checker) para consultar qualquer código enquanto lê.

J-Kit18 min de leituraIntermediário
  • HTTP
  • Códigos de status
  • API
  • SEO
  • Cache

Resumo rápido

  • O primeiro dígito diz de que lado está o problema: 4xx é o cliente, 5xx é o servidor. É a triagem mais rápida que existe.
  • 301 e 302 podem trocar POST por GET; 307 e 308 preservam o método e o corpo. Trocar um pelo outro quebra APIs.
  • 401 é “não sei quem você é” e EXIGE o header WWW-Authenticate; 403 é “sei quem você é e você não pode”.
  • Uma página inexistente que devolve 200 é um soft 404, um bug de SEO. Devolva 404 ou 410 de verdade.

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.

As cinco classes de status e o significado semântico de cada uma (RFC 9110 §15).
ClasseSignificadoDe quem é o problema
1xxInformativo: resposta provisória, o pedido continua.De ninguém, é um aviso intermediário.
2xxSucesso: o pedido foi recebido, entendido e aceito.De ninguém, deu certo.
3xxRedirecionamento: falta uma ação para concluir (ir a outra URL, usar o cache).Compartilhado: siga a instrução.
4xxErro do cliente: sintaxe, autenticação, permissão ou recurso.Seu pedido, conserte a requisição.
5xxErro 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.

  1. 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).

  2. Janeiro de 1997RFC 2068, HTTP/1.1

    O primeiro recorte padronizado do HTTP/1.1, com conexões persistentes e mais códigos.

  3. Junho de 1999RFC 2616, HTTP/1.1 revisada

    A revisão que foi a referência por quinze anos, num único documento monolítico.

  4. Junho de 2014RFC 7230–7235

    A 2616 é quebrada em seis documentos (sintaxe, semântica, condicionais, range, cache, autenticação).

  5. 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.

Os quatro redirects que todo mundo troca. Método preservado, cache padrão (RFC 9110 §15.1) e o que o crawler faz.
CódigoDuraçãoMétodo preservado?Cacheável por padrão?O que o crawler faz
301 Moved PermanentlyPermanenteNão, pode trocar POST→GETSimConsolida os sinais na nova URL
308 Permanent RedirectPermanenteSim, mantém método e corpoSimConsolida os sinais na nova URL
302 FoundTemporárioNão, pode trocar POST→GETNãoMantém a URL original indexada
307 Temporary RedirectTemporárioSim, mantém método e corpoNãoManté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=cartao
Com 302, o POST vira GET e o corpo some. Com 307, método e corpo sobrevivem ao redirect. Para uma migração permanente de endpoint de API, a mesma lógica vale entre 301 e 308.

A 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.

Busque um código (ex.: 308) e veja significado, RFC, se é cacheável por padrão, causas comuns e como tratar, tudo no navegador.Abrir a ferramenta em página inteira

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.

404 vs 410: o que muda para o backend e para o crawler.
Aspecto404 Not Found410 Gone
SignificadoNão existe nessa URL (ou o servidor não revela).Existiu e foi removido em definitivo.
IntençãoPode ser erro de rota ou algo passageiro.Declaração explícita de “não volta”.
Cacheável por padrãoSimSim
O que o Googlebot fazRemove 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)
A requisição condicional com If-None-Match e a resposta 304. O servidor confirma que o ETag ainda bate e não reenvia o corpo.

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.

200 OK (corpo completo)46.080 bytes
304 Not Modified (revalidação)180 bytes
Bytes que trafegam para o mesmo arquivo de 45 KB: a resposta completa (200) contra a revalidação (304). A barra minúscula do 304 é o argumento.
Ver os dados
CategoriaValor
200 OK (corpo completo)46.080 bytes
304 Not Modified (revalidação)180 bytes
46.080 → 180bytes: resposta completa vs. revalidação 304
~99,6%banda economizada nessa revalidação
0bytes de corpo em um 304 (proibido pela RFC 9110 §15.4.5)

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.

Os quatro erros de servidor mais comuns e o que cada um costuma significar.
CódigoO que costuma serTemporário?
500 Internal Server ErrorExceção não tratada; bug na aplicação.Não diz nada sobre retry
502 Bad GatewayResposta inválida do upstream (app caiu).Talvez
503 Service UnavailableIndisponibilidade momentânea (deploy, sobrecarga).Sim, aceita Retry-After
504 Gateway TimeoutO 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?
Se a mudança é definitiva, use 301: ele é permanente, cacheável por padrão e transfere a autoridade de SEO para a nova URL (o Google trata 301 e 308 como equivalentes). O 302 é para desvios temporários e mantém a URL antiga indexada. Só cuide do método: tanto 301 quanto 302 podem trocar um POST por GET.
Quando usar 307/308 em vez de 302/301?
Sempre que o método e o corpo precisarem sobreviver ao redirect. 307 (temporário) e 308 (permanente) preservam o POST; 302 e 301 podem convertê-lo em GET e descartar o corpo. Em APIs, redirecionar um POST com 302 é um bug clássico: use 307 para desvios passageiros e 308 para mover um endpoint em definitivo.
Qual a diferença entre 401 e 403?
401 significa que você não está autenticado, “não sei quem você é”, e a RFC 9110 §15.5.2 exige que a resposta traga o header WWW-Authenticate. 403 significa que você está autenticado, mas não tem permissão, “sei quem você é e você não pode”. Reautenticar pode resolver um 401; não resolve um 403.
404 ou 410 para uma página removida? O Google trata diferente?
Use 410 Gone quando quiser declarar que a remoção é definitiva; 404 Not Found serve para “não existe aqui”, que pode ser passageiro. Apesar do folclore de que o 410 sai mais rápido do índice, a documentação do Google diz tratar todos os 4xx (exceto 429) igual: os dois levam à remoção com o tempo. O erro grave é devolver 200 numa página inexistente, o que gera um soft 404.
O que é um soft 404 e por que é um problema?
É quando uma página que não existe responde com HTTP 200, mas o corpo diz “não encontrado” ou está vazio. O Search Console sinaliza isso como soft 404. É um problema porque desperdiça orçamento de rastreamento e polui o índice com páginas sem valor. A correção é servir o status certo na linha de status: 404 ou 410 de verdade, não um 200 disfarçado.
O que fazer ao receber um 429?
Você atingiu um limite de requisições (RFC 6585 §4). Respeite o header Retry-After, se presente, ele vem em segundos ou como uma data HTTP (RFC 9110 §10.2.3). Se não vier, aplique backoff exponencial (1 s, 2 s, 4 s...) com um pouco de jitter para não sincronizar clientes, e defina um teto de espera. Nunca fique repetindo a chamada no mesmo ritmo.
O 418 “I’m a teapot” é um código real?
É real como registro, mas nasceu de uma piada. Ele vem da RFC 2324 (HTCPCP), publicada em 1º de abril de 1998. Não faz parte do HTTP sério, mas a IANA o marca como (Unused) e a RFC 9110 §15.5.19 o reserva para preservar a brincadeira. Alguns serviços o usam como easter egg. Não devolva 418 para erros de verdade, use o código que descreve o que aconteceu.

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

  1. RFC 9110, HTTP Semantics (status codes, Retry-After, WWW-Authenticate)
  2. RFC 9111, HTTP Caching (validação, ETag, 304)
  3. RFC 6585, Additional HTTP Status Codes (428, 429, 431, 511)
  4. RFC 7538, 308 Permanent Redirect
  5. RFC 2324, HTCPCP/1.0 (a origem do 418)
  6. IANA, Registro oficial de códigos de status HTTP
  7. Google Search Central, Códigos de status HTTP e soft 404