Desenvolvimento web e mobile

Códigos de status HTTP: referência completa com significado e uso correto

Iniciante17 min de leituraAtualizado

Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês

O código de status é a primeira coisa que um cliente descobre sobre uma resposta e, muitas vezes, a única que um intermediário lê; por isso, escolher o código errado quebra silenciosamente caches, retries e tratamento de erros. Esta referência explica o modelo por trás das cinco classes, lista em tabelas cada código registrado com seu significado e uso adequado e, em seguida, percorre os pares que costumam ser confundidos, as regras de redirecionamento e de cache e como escolher códigos ao projetar uma API.

O que você vai aprender

  • Explicar a estrutura de uma linha de status e o que cada uma das cinco classes sinaliza
  • Consultar qualquer código de status e saber quando ele é a resposta correta
  • Escolher corretamente entre os pares que vivem sendo confundidos, como 301 e 308 ou 401 e 403
  • Aplicar a semântica de redirecionamento sem mudar o método de uma requisição por acidente
  • Prever quais respostas podem ir para o cache e como requisições condicionais produzem um 304
  • Escolher códigos de status adequados ao projetar uma API HTTP e reportar erros de forma consistente

Toda resposta HTTP começa com um número de três dígitos, e esse número é a única parte da resposta que muitos sistemas chegam a ler. É com ele que o navegador decide se segue um redirecionamento, que o cache decide se pode armazenar o corpo, que o load balancer decide se o backend está saudável e que a biblioteca cliente decide se lança uma exceção ou tenta de novo. Escolher o código errado normalmente não gera um erro visível: gera um erro sutil, meses depois, quando um proxy guarda em cache algo que não deveria ou um loop de retry martela um endpoint que nunca ia responder com sucesso.

A especificação de referência é a RFC 9110, que substituiu a antiga família da RFC 7231 e hoje é o documento único que descreve a semântica do HTTP nas versões 1.1, 2 e 3. A lista oficial de códigos atribuídos é o IANA HTTP Status Code Registry. Esta página é um complemento prático aos dois: o que cada código significa, quando ele é a resposta certa e onde os desenvolvedores erram com frequência.

A linha de status e as cinco classes

No HTTP/1.1, uma resposta começa com a versão do protocolo, o código de status e uma reason phrase (a frase descritiva):

HTTP/1.1 404 Not Found
Content-Type: application/problem+json
Content-Length: 91

{"type":"https://example.com/errors/no-such-order","title":"Order not found","status":404}

A reason phrase é apenas informativa. Ela existe para humanos que leem um fluxo bruto, os clientes não devem agir com base nela, e o HTTP/2 e o HTTP/3 a removeram por completo: eles carregam só o código numérico, como um pseudo-header. Nunca faça parsing dela.

O primeiro dígito é a parte com significado definido, e ele funciona como um contrato: um cliente que recebe um código desconhecido deve tratá-lo como o x00 genérico da sua classe. Uma resposta 499 é tratada como 400, e uma 572 como 500. É essa regra que permite registrar novos códigos sem quebrar clientes existentes, e é por isso que escolher um número não registrado de aparência plausível causa menos estrago do que escolher um código registrado com o significado errado — mas ainda é pior do que escolher o certo.

ClasseNomeSignificado
1xxInformationalA requisição foi recebida; o processamento continua. Uma resposta provisória.
2xxSuccessfulA requisição foi recebida, entendida e aceita.
3xxRedirectionÉ preciso uma ação adicional para concluir a requisição.
4xxClient errorA requisição tem um problema e vai falhar de novo se não for alterada.
5xxServer errorA requisição pode ser válida; o servidor não conseguiu atendê-la.

A fronteira entre 4xx e 5xx tem peso operacional. É ela que separa "culpa sua" de "culpa nossa" em um dashboard, é o que os health checks observam e é a base de cálculo dos error budgets. Retornar 500 para uma requisição malformada polui seus próprios alertas; retornar 400 para uma falha interna esconde uma indisponibilidade real.

1xx — Informational

Respostas provisórias, enviadas antes da resposta final. Um cliente que não entende um 1xx deve ignorá-lo e continuar esperando.

CódigoNomeQuando aparece
100ContinueO cliente enviou Expect: 100-continue só com os headers, perguntando se deve mandar um corpo grande. O servidor responde 100 para convidá-lo, ou um 4xx para recusar antes de o upload começar.
101Switching ProtocolsO servidor concorda em trocar de protocolo, como no handshake de WebSocket depois de Upgrade: websocket.
102ProcessingResposta de espera do WebDAV para operações longas. Descontinuado; use 103 ou mantenha a conexão aberta.
103Early HintsEnviado antes da resposta final para transportar headers Link, de modo que o navegador possa fazer preload ou preconnect enquanto o servidor ainda está trabalhando.

Early Hints é o único dos quatro que vale a pena adicionar de propósito a uma aplicação moderna: numa página lenta renderizada no servidor, ele permite que o navegador comece a baixar folhas de estilo e fontes antes mesmo de o HTML existir.

2xx — Successful

CódigoNomeQuando usar
200OKO sucesso padrão. O corpo traz o resultado da requisição.
201CreatedUm novo recurso foi criado. Inclua um header Location apontando para ele e, normalmente, a representação no corpo.
202AcceptedA requisição foi aceita para processamento assíncrono e o resultado ainda não é conhecido. Retorne uma forma de acompanhar o andamento.
203Non-Authoritative InformationUm proxy de transformação modificou o payload. Raro fora de intermediários.
204No ContentSucesso sem nada a retornar: um DELETE, ou um PUT cujo estado resultante o cliente já conhece. Não pode haver corpo.
205Reset ContentSucesso; o cliente deve limpar o formulário que gerou a requisição. Raramente usado.
206Partial ContentUma requisição Range bem-sucedida. É a base dos downloads retomáveis e da navegação dentro de mídias.
207Multi-StatusWebDAV: um corpo XML contendo vários códigos de status independentes.
208Already ReportedWebDAV: os membros de uma coleção já foram enumerados.
226IM UsedA resposta é o resultado da aplicação de instance manipulations, como uma codificação delta.

O par que importa no dia a dia é 201 versus 200. Se a requisição criou algo endereçável, diga isso e informe o endereço: assim clientes e caches podem trabalhar com o novo recurso sem adivinhar a URL. E 204 significa exatamente nenhum corpo: mandar JSON junto viola a especificação, e alguns clientes reagem lançando exceção.

3xx — Redirection

CódigoNomePreserva o método?Quando usar
300Multiple Choicesn/aExistem várias representações e o cliente deve escolher. Quase nunca usado.
301Moved PermanentlyNão, na práticaO recurso tem uma nova URL permanente. Historicamente transforma POST em GET.
302FoundNão, na práticaMudança temporária. Também transforma POST em GET em todos os clientes reais.
303See OtherNão, por design"Busque o resultado nesta outra URL com GET." A resposta correta ao envio concluído de um formulário.
304Not Modifiedn/aA cópia em cache continua válida; não há corpo. Resposta a uma requisição condicional.
305Use Proxyn/aDescontinuado por motivos de segurança. Não use.
306(sem uso)n/aReservado; era "Switch Proxy".
307Temporary RedirectSimMudança temporária, com método e corpo preservados.
308Permanent RedirectSimMudança permanente, com método e corpo preservados.

Acertando nos redirecionamentos

Vale a pena entender a bagunça histórica, porque ela ainda molda o comportamento. A RFC 2616 dizia que 301 e 302 não deviam mudar o método da requisição sem confirmação do usuário. Os navegadores ignoraram isso e transformaram em GET todo POST redirecionado, até que a especificação cedeu e documentou o comportamento real. Com isso, não sobrou nenhuma forma de dizer "mudou de lugar, e mantenha o método" — e é por isso que 307 e 308 foram criados.

As regras práticas:

  • 301 versus 308. Os dois são permanentes e os dois ficam em cache nos clientes, muitas vezes indefinidamente, então nenhum deve ser usado sem pensar. Escolha 301 para mudanças comuns de site, redirecionamentos para o host e o esquema canônicos e páginas que mudaram de endereço, porque ele tem suporte universal e os mecanismos de busca construíram décadas de comportamento em torno dele. Escolha 308 quando o endpoint aceita POST, PUT ou PATCH e o método precisa sobreviver, como na mudança de caminho de uma API versionada.
  • 302 versus 307. Os dois são temporários. O 302 vai converter um POST em GET; o 307, não. Se você está redirecionando o envio de um formulário e quer que o navegador refaça o POST no novo endereço, precisa do 307. Se quer o comportamento seguro, o 303 o declara explicitamente em vez de depender de uma regra de fato.
  • 303 depois de uma escrita. O padrão Post/Redirect/Get existe para impedir que um refresh do navegador reenvie um pagamento. Responda ao POST bem-sucedido com 303 e um Location apontando para a página de confirmação.

Dois cuidados operacionais. Redirecionamentos permanentes são difíceis de desfazer: um navegador que guardou um 301 em cache pode passar muito tempo sem perguntar de novo, então teste primeiro com um código temporário e promova-o quando tiver certeza. E cada redirecionamento custa uma ida e volta: cadeias de três ou quatro saltos, comuns depois de várias migrações de site, são um problema de desempenho mensurável e devem ser reduzidas a um único salto. Quando você estiver depurando um redirecionamento que estraga os parâmetros da query string, o codificador/decodificador de URL deixa a diferença visível na hora.

4xx — Client error

CódigoNomeQuando usar
400Bad RequestA requisição está malformada: sintaxe inválida, corpo que não pode ser interpretado, framing incorreto. Não é um erro genérico para tudo.
401UnauthorizedA autenticação está ausente ou é inválida. Precisa incluir WWW-Authenticate.
402Payment RequiredReservado; algumas APIs o usam para falhas de cota ou de cobrança.
403ForbiddenO servidor entendeu a identidade e recusa. Autenticar de novo não vai adiantar.
404Not FoundNão há recurso nesta URL, ou o servidor prefere não dizer se existe um.
405Method Not AllowedA URL existe, mas não para este método. Precisa incluir um header Allow.
406Not AcceptableNenhuma representação corresponde aos headers Accept.
407Proxy Authentication RequiredComo o 401, mas para um proxy; usa Proxy-Authenticate.
408Request TimeoutO cliente demorou demais para enviar a requisição. Pode ser repetido com segurança.
409ConflictA requisição entra em conflito com o estado atual: uma chave duplicada, uma edição concorrente, uma transição de estado inválida.
410GoneRemovido de propósito e não vai voltar. Mais forte e mais útil que o 404 quando você sabe disso.
411Length RequiredA requisição precisa de um Content-Length.
412Precondition FailedUma pré-condição If-Match ou If-Unmodified-Since não foi atendida. É a base do lock otimista.
413Content Too LargeO corpo excede o limite do servidor. Antes se chamava "Payload Too Large".
414URI Too LongO alvo da requisição excede o limite do servidor.
415Unsupported Media TypeO Content-Type não é um dos aceitos pelo endpoint.
416Range Not SatisfiableO intervalo de bytes pedido está fora do recurso.
417Expectation FailedO header Expect não pode ser atendido.
418I'm a teapotUma pegadinha de 1º de abril de 1998, reservada pela IANA para nunca ser atribuída.
421Misdirected RequestA conexão chegou a um servidor incapaz de produzir uma resposta para esta autoridade. Aparece com o connection coalescing do HTTP/2.
422Unprocessable ContentSintaticamente válida, semanticamente errada. A escolha usual para falhas de validação de regras de negócio.
423LockedWebDAV: o recurso está bloqueado.
424Failed DependencyWebDAV: uma requisição da qual esta depende falhou.
425Too EarlyO servidor não vai processar uma requisição com early data que pode ter sido repetida.
426Upgrade RequiredO cliente precisa trocar de protocolo, por exemplo para TLS.
428Precondition RequiredO servidor exige uma requisição condicional, para evitar atualizações perdidas.
429Too Many RequestsLimite de taxa atingido (rate limiting). Inclua Retry-After.
431Request Header Fields Too LargeOs headers excedem o limite — geralmente por causa de um cookie grande demais.
451Unavailable For Legal ReasonsBloqueado por exigência legal. O número é uma referência a Fahrenheit 451.

5xx — Server error

CódigoNomeQuando usar
500Internal Server ErrorUma falha não tratada. O código genérico — nunca a resposta deliberada a uma situação conhecida.
501Not ImplementedO servidor não suporta o método de forma alguma.
502Bad GatewayUm gateway recebeu uma resposta inválida de um servidor upstream.
503Service UnavailableTemporariamente incapaz de atender a requisição — sobrecarga ou manutenção. Inclua Retry-After.
504Gateway TimeoutUm gateway não recebeu a tempo uma resposta de um servidor upstream.
505HTTP Version Not SupportedA versão do protocolo usada na requisição não é suportada.
506Variant Also NegotiatesUm erro de configuração na negociação de conteúdo.
507Insufficient StorageWebDAV: não há espaço suficiente para concluir a requisição.
508Loop DetectedWebDAV: um loop infinito durante o processamento.
510Not ExtendedObsoleto; o mecanismo de extensões ao qual pertencia nunca foi adotado.
511Network Authentication RequiredUm portal cativo exigindo login. Deve ser gerado pelo proxy que intercepta, nunca por um servidor de origem.

Os pares que vivem sendo confundidos

401 versus 403. O 401 diz "não sei quem você é, ou suas credenciais foram recusadas" e obriga o servidor a enviar WWW-Authenticate. O 403 diz "sei quem você é e a resposta continua sendo não". A consequência prática está no comportamento do cliente: um 401 deve disparar uma tela de login ou a renovação do token; um 403, não. Muitas APIs retornam 403 para tokens expirados, o que joga os clientes num estado de falha permanente em vez de uma renovação.

Existe uma terceira opção que vale conhecer. Quando até a existência de um recurso é confidencial, retornar 404 para quem não tem autorização é uma medida de hardening legítima: evita confirmar que existe um objeto com aquele identificador.

400 versus 422. O 400 trata da forma; o 422, do significado. Um corpo que não é JSON válido é um 400; um corpo JSON válido cujo endDate é anterior ao startDate é um 422. Algumas APIs bem conhecidas usam 400 para tudo e continuam perfeitamente utilizáveis, então a propriedade que realmente importa é a consistência, somada a um corpo que aponte os campos problemáticos. A RFC 9457 define exatamente para isso um media type padrão de problem details, que vale a pena adotar para que os clientes interpretem os erros de forma uniforme.

404 versus 410. O 404 significa "não há nada aqui, e não vou dizer se já houve". O 410 significa "isto existiu e foi removido de propósito". Se você sabe que o recurso sumiu de vez, o 410 é mais informativo: os crawlers descartam a URL mais rápido e os clientes podem parar de tentar de novo.

409 versus 422 versus 412. O 409 é um conflito com o estado atual do recurso: criar um usuário cujo e-mail já existe, ou mudar o status de um pedido que já foi enviado. O 422 é um problema do conteúdo enviado em si. O 412 é a resposta a uma pré-condição que falhou numa requisição condicional, que é como se implementa o controle de concorrência otimista com ETag e If-Match.

502 versus 503 versus 504. Os três costumam vir de um proxy, e não da sua aplicação, e diferenciá-los é o jeito mais rápido de localizar um incidente. O 502 significa que o upstream respondeu algo ininteligível — muitas vezes um processo que caiu ou uma incompatibilidade de protocolo. O 503 significa que o servidor está de pé e está recusando de propósito neste momento: sobrecarregado, em manutenção ou sem nenhum backend saudável no pool. O 504 significa que o upstream não respondeu a tempo, o que aponta para uma dependência lenta, um deadlock ou um timeout configurado mais baixo no proxy do que na aplicação. Quando há um gateway na frente dos seus serviços, a configuração de timeout e retry dele determina qual dos três seus usuários vão ver — veja gerenciamento de APIs e gateways.

200 com um erro dentro. O antipadrão mais danoso da lista. Todo intermediário, todo sistema de monitoramento e todo cliente genérico o trata como sucesso, então as falhas somem dos dashboards e a lógica de retry nunca é acionada. Coloque o resultado no código de status e o detalhe no corpo.

Cache e requisições condicionais

A RFC 9111 define quais respostas podem ser armazenadas, e o código de status é o ponto de partida. São cacheáveis por padrão — ou seja, um cache compartilhado pode armazená-las mesmo sem Cache-Control explícito — os códigos 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414 e 501. Todo o resto exige permissão explícita por meio de diretivas de cache. Essa lista traz duas surpresas que vale internalizar: 404 e 301 são cacheáveis por padrão, então um deploy temporariamente quebrado que retorna 404, ou um redirecionamento publicado por engano, pode continuar lembrado por caches e navegadores muito depois de você corrigir a origem.

Na prática, não confie nos padrões. O Cache-Control da resposta resolve a questão: no-store proíbe qualquer armazenamento, no-cache permite armazenar mas exige revalidação antes de reutilizar, private restringe ao navegador, max-age define o tempo de validade em segundos e must-revalidate proíbe servir uma cópia vencida depois da expiração.

É das requisições condicionais que vem o 304. O servidor envia um validador — um ETag ou um Last-Modified — e o cliente o devolve na requisição seguinte:

GET /api/orders/42 HTTP/1.1
Host: example.com
If-None-Match: "a3f1c0"

HTTP/1.1 304 Not Modified
ETag: "a3f1c0"
Cache-Control: max-age=60

Um 304 não tem corpo, e é justamente essa a ideia: o cliente já tem o conteúdo e só precisava saber se ele continua válido. Os mesmos validadores alimentam o If-Match nas escritas, transformando uma condição de corrida com atualização perdida num 412 limpo.

Escolhendo códigos ao projetar uma API

Um conjunto pequeno e escolhido com intenção é melhor que uma cobertura exaustiva. A maioria das APIs bem projetadas usa mais ou menos uma dúzia de códigos: 200, 201, 202, 204, 301 ou 308 para endpoints que mudaram de lugar, 400, 401, 403, 404, 409, 422, 429, e 500 com 503 para degradação. Registre-os como parte do contrato da API para que todos os endpoints se comportem da mesma forma e os clientes possam tratar erros de maneira genérica.

Além da escolha em si, alguns hábitos compensam. Retorne um corpo de erro legível por máquina num formato único e documentado — problem details da RFC 9457, com type, title, status, detail e eventuais erros por campo — para que o cliente possa exibir uma mensagem sem tratar cada endpoint como caso especial. Não vaze stack traces nem identificadores internos nesse corpo; registre-os no log com um correlation id e retorne o id. Envie Retry-After com 429 e 503 para que os clientes esperem de forma inteligente em vez de chutar, e garanta que seu próprio código cliente o respeite. Inclua Allow com 405 e WWW-Authenticate com 401, ambos exigidos pela especificação. Prefira operações idempotentes sempre que possível, porque é a idempotência que torna seguro fazer retry de um 500 ou de um 504. E lembre-se de que o fetch no navegador resolve a promise para qualquer status, então o código cliente precisa verificar o status explicitamente — um ponto abordado em fundamentos de JavaScript, enquanto o HttpClient do Angular segue a convenção oposta e emite um erro.

Por fim, trate os códigos de status como um instrumento operacional, e não só como um contrato com o cliente. Monte gráficos separados para as taxas de 4xx e de 5xx, crie alertas para 5xx e fique de olho nos códigos específicos que indicam algo estrutural: um aumento de 401 depois de um deploy geralmente indica um problema de token ou de relógio, um aumento de 504 indica que uma dependência ficou mais lenta, e um aumento de 429 indica um cliente abusivo ou um limite apertado demais. As considerações de design mais amplas por trás dessas escolhas estão em design de APIs REST, e o lado do framework em construindo APIs REST com Spring Boot.

Principais pontos

  • O primeiro dígito é o contrato: códigos desconhecidos são tratados como o x00 genérico da sua classe, e a divisão entre 4xx e 5xx decide nos seus dashboards de quem é a culpa de um incidente.
  • Use 307 e 308 quando um redirecionamento precisa preservar o método e o corpo; 301 e 302 transformam POST em GET na prática, e o 303 declara isso explicitamente depois de uma escrita.
  • 401 quer dizer "autentique-se", 403 quer dizer "não"; 400 quer dizer malformada, 422 quer dizer semanticamente inválida; 502, 503 e 504 distinguem um upstream quebrado, um servidor que recusa e um upstream lento.
  • Um 200 com um erro no corpo anula caches, retries e monitoramento — coloque o resultado na linha de status e o detalhe num corpo problem details da RFC 9457.
  • 200, 301, 404 e vários outros são cacheáveis por padrão, então um redirecionamento equivocado ou um 404 passageiro pode sobreviver ao deploy que o causou, a menos que o Cache-Control diga o contrário.
  • Escolha uma dúzia de códigos, documente-os no contrato da API, sempre envie Retry-After, Allow e WWW-Authenticate onde forem exigidos e verifique o código explicitamente no código cliente.

Fontes e leituras complementares

Perguntas frequentes

Qual é a diferença entre 401 e 403?
401 Unauthorized significa que a requisição não trazia uma autenticação válida, ou que as credenciais enviadas não foram aceitas; a resposta precisa incluir um header WWW-Authenticate dizendo ao cliente como se autenticar, e tentar de novo com credenciais melhores pode dar certo. 403 Forbidden significa que o servidor sabe quem você é e recusa mesmo assim. Repetir um 403 com a mesma identidade nunca vai funcionar, então o cliente não deve pedir um novo login.
Uma falha de validação deve retornar 400 ou 422?
Use 400 Bad Request quando a própria requisição está malformada: JSON quebrado, um header obrigatório ausente, um parâmetro que não pode ser interpretado. Use 422 Unprocessable Content quando a sintaxe está correta, mas o conteúdo está semanticamente errado, como uma data de término anterior à de início ou uma moeda que a conta não pode usar. Os dois são legítimos; o que mais importa é que sua API seja consistente e retorne um corpo legível por máquina explicando qual campo falhou.
É aceitável retornar 200 com um erro no corpo?
É tecnicamente permitido e quase sempre um erro. Caches, proxies, políticas de retry, as devtools do navegador, dashboards de monitoramento e bibliotecas cliente se baseiam no código de status, então uma falha disfarçada de 200 fica invisível para todas as camadas, exceto o código que interpreta o seu envelope específico. Use o código de status para o resultado e o corpo para o detalhe, de preferência no formato problem details da RFC 9457.
Quais códigos de status um cliente deve tentar de novo?
Faça retry em 408, 429, 502, 503 e 504, porque indicam uma condição transitória, e respeite qualquer header Retry-After antes de aplicar backoff exponencial com jitter. Não faça retry nos demais códigos 4xx: eles descrevem um problema na requisição que continuará lá na próxima vez. Tentar de novo um 500 só é seguro se a operação for idempotente, já que o servidor pode tê-la aplicado parcialmente.