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.
| Classe | Nome | Significado |
|---|---|---|
| 1xx | Informational | A requisição foi recebida; o processamento continua. Uma resposta provisória. |
| 2xx | Successful | A requisição foi recebida, entendida e aceita. |
| 3xx | Redirection | É preciso uma ação adicional para concluir a requisição. |
| 4xx | Client error | A requisição tem um problema e vai falhar de novo se não for alterada. |
| 5xx | Server error | A 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ódigo | Nome | Quando aparece |
|---|---|---|
| 100 | Continue | O 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. |
| 101 | Switching Protocols | O servidor concorda em trocar de protocolo, como no handshake de WebSocket depois de Upgrade: websocket. |
| 102 | Processing | Resposta de espera do WebDAV para operações longas. Descontinuado; use 103 ou mantenha a conexão aberta. |
| 103 | Early Hints | Enviado 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ódigo | Nome | Quando usar |
|---|---|---|
| 200 | OK | O sucesso padrão. O corpo traz o resultado da requisição. |
| 201 | Created | Um novo recurso foi criado. Inclua um header Location apontando para ele e, normalmente, a representação no corpo. |
| 202 | Accepted | A requisição foi aceita para processamento assíncrono e o resultado ainda não é conhecido. Retorne uma forma de acompanhar o andamento. |
| 203 | Non-Authoritative Information | Um proxy de transformação modificou o payload. Raro fora de intermediários. |
| 204 | No Content | Sucesso sem nada a retornar: um DELETE, ou um PUT cujo estado resultante o cliente já conhece. Não pode haver corpo. |
| 205 | Reset Content | Sucesso; o cliente deve limpar o formulário que gerou a requisição. Raramente usado. |
| 206 | Partial Content | Uma requisição Range bem-sucedida. É a base dos downloads retomáveis e da navegação dentro de mídias. |
| 207 | Multi-Status | WebDAV: um corpo XML contendo vários códigos de status independentes. |
| 208 | Already Reported | WebDAV: os membros de uma coleção já foram enumerados. |
| 226 | IM Used | A 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ódigo | Nome | Preserva o método? | Quando usar |
|---|---|---|---|
| 300 | Multiple Choices | n/a | Existem várias representações e o cliente deve escolher. Quase nunca usado. |
| 301 | Moved Permanently | Não, na prática | O recurso tem uma nova URL permanente. Historicamente transforma POST em GET. |
| 302 | Found | Não, na prática | Mudança temporária. Também transforma POST em GET em todos os clientes reais. |
| 303 | See Other | Não, por design | "Busque o resultado nesta outra URL com GET." A resposta correta ao envio concluído de um formulário. |
| 304 | Not Modified | n/a | A cópia em cache continua válida; não há corpo. Resposta a uma requisição condicional. |
| 305 | Use Proxy | n/a | Descontinuado por motivos de segurança. Não use. |
| 306 | (sem uso) | n/a | Reservado; era "Switch Proxy". |
| 307 | Temporary Redirect | Sim | Mudança temporária, com método e corpo preservados. |
| 308 | Permanent Redirect | Sim | Mudanç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,PUTouPATCHe 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
POSTemGET; o 307, não. Se você está redirecionando o envio de um formulário e quer que o navegador refaça oPOSTno 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
POSTbem-sucedido com 303 e umLocationapontando 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ódigo | Nome | Quando usar |
|---|---|---|
| 400 | Bad Request | A requisição está malformada: sintaxe inválida, corpo que não pode ser interpretado, framing incorreto. Não é um erro genérico para tudo. |
| 401 | Unauthorized | A autenticação está ausente ou é inválida. Precisa incluir WWW-Authenticate. |
| 402 | Payment Required | Reservado; algumas APIs o usam para falhas de cota ou de cobrança. |
| 403 | Forbidden | O servidor entendeu a identidade e recusa. Autenticar de novo não vai adiantar. |
| 404 | Not Found | Não há recurso nesta URL, ou o servidor prefere não dizer se existe um. |
| 405 | Method Not Allowed | A URL existe, mas não para este método. Precisa incluir um header Allow. |
| 406 | Not Acceptable | Nenhuma representação corresponde aos headers Accept. |
| 407 | Proxy Authentication Required | Como o 401, mas para um proxy; usa Proxy-Authenticate. |
| 408 | Request Timeout | O cliente demorou demais para enviar a requisição. Pode ser repetido com segurança. |
| 409 | Conflict | A requisição entra em conflito com o estado atual: uma chave duplicada, uma edição concorrente, uma transição de estado inválida. |
| 410 | Gone | Removido de propósito e não vai voltar. Mais forte e mais útil que o 404 quando você sabe disso. |
| 411 | Length Required | A requisição precisa de um Content-Length. |
| 412 | Precondition Failed | Uma pré-condição If-Match ou If-Unmodified-Since não foi atendida. É a base do lock otimista. |
| 413 | Content Too Large | O corpo excede o limite do servidor. Antes se chamava "Payload Too Large". |
| 414 | URI Too Long | O alvo da requisição excede o limite do servidor. |
| 415 | Unsupported Media Type | O Content-Type não é um dos aceitos pelo endpoint. |
| 416 | Range Not Satisfiable | O intervalo de bytes pedido está fora do recurso. |
| 417 | Expectation Failed | O header Expect não pode ser atendido. |
| 418 | I'm a teapot | Uma pegadinha de 1º de abril de 1998, reservada pela IANA para nunca ser atribuída. |
| 421 | Misdirected Request | A conexão chegou a um servidor incapaz de produzir uma resposta para esta autoridade. Aparece com o connection coalescing do HTTP/2. |
| 422 | Unprocessable Content | Sintaticamente válida, semanticamente errada. A escolha usual para falhas de validação de regras de negócio. |
| 423 | Locked | WebDAV: o recurso está bloqueado. |
| 424 | Failed Dependency | WebDAV: uma requisição da qual esta depende falhou. |
| 425 | Too Early | O servidor não vai processar uma requisição com early data que pode ter sido repetida. |
| 426 | Upgrade Required | O cliente precisa trocar de protocolo, por exemplo para TLS. |
| 428 | Precondition Required | O servidor exige uma requisição condicional, para evitar atualizações perdidas. |
| 429 | Too Many Requests | Limite de taxa atingido (rate limiting). Inclua Retry-After. |
| 431 | Request Header Fields Too Large | Os headers excedem o limite — geralmente por causa de um cookie grande demais. |
| 451 | Unavailable For Legal Reasons | Bloqueado por exigência legal. O número é uma referência a Fahrenheit 451. |
5xx — Server error
| Código | Nome | Quando usar |
|---|---|---|
| 500 | Internal Server Error | Uma falha não tratada. O código genérico — nunca a resposta deliberada a uma situação conhecida. |
| 501 | Not Implemented | O servidor não suporta o método de forma alguma. |
| 502 | Bad Gateway | Um gateway recebeu uma resposta inválida de um servidor upstream. |
| 503 | Service Unavailable | Temporariamente incapaz de atender a requisição — sobrecarga ou manutenção. Inclua Retry-After. |
| 504 | Gateway Timeout | Um gateway não recebeu a tempo uma resposta de um servidor upstream. |
| 505 | HTTP Version Not Supported | A versão do protocolo usada na requisição não é suportada. |
| 506 | Variant Also Negotiates | Um erro de configuração na negociação de conteúdo. |
| 507 | Insufficient Storage | WebDAV: não há espaço suficiente para concluir a requisição. |
| 508 | Loop Detected | WebDAV: um loop infinito durante o processamento. |
| 510 | Not Extended | Obsoleto; o mecanismo de extensões ao qual pertencia nunca foi adotado. |
| 511 | Network Authentication Required | Um 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
x00gené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
POSTemGETna 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-Controldiga o contrário. - Escolha uma dúzia de códigos, documente-os no contrato da API, sempre envie
Retry-After,AlloweWWW-Authenticateonde 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.