HTTP status codes explained: a practical reference

A practical guide to the HTTP status codes you actually meet, grouped by class, with advice on choosing the right code for APIs and websites.

How status codes are organized

Every HTTP response starts with a three-digit status code that summarizes the outcome of the request. The first digit defines the class, which tells a client how to react even when it does not recognize the specific code.

ClassMeaningClient should
1xxInformationalKeep waiting for the final response
2xxSuccessUse the response
3xxRedirectionFollow the new location or use a cached copy
4xxClient errorFix the request before retrying
5xxServer errorRetry later, or report the problem

The status code and the response body should agree. An API that returns 200 OK with an error message in the body forces every client to parse the body to discover failure, and it breaks monitoring, caching, and retry logic that rely on the code.

2xx: success

CodeNameWhen to use it
200OKThe request succeeded and the body contains the result
201CreatedA new resource was created; include a Location header pointing to it
202AcceptedThe work was queued and will finish later; return a way to check status
204No ContentThe request succeeded and there is nothing to return, common for deletes

Choosing precisely helps clients. 201 with a Location header lets a client find the new resource without guessing its URL, and 202 makes it clear that success does not mean the job is finished.

3xx: redirects and caching

CodeNameWhen to use it
301Moved PermanentlyA URL has changed for good, such as after a site restructure
302FoundA temporary redirect; the original URL remains canonical
303See OtherAfter a form POST, send the browser to a result page with GET
304Not ModifiedThe cached copy is still valid; no body is sent
307Temporary RedirectLike 302, but the method and body must be kept
308Permanent RedirectLike 301, but the method and body must be kept

Older clients changed a POST into a GET when following 301 and 302 redirects. The newer 307 and 308 codes guarantee that the method is preserved, which matters for APIs that redirect non-GET requests.

4xx: client errors

CodeNameWhen to use it
400Bad RequestThe request is malformed, such as invalid JSON or a missing parameter
401UnauthorizedAuthentication is missing or invalid; include a WWW-Authenticate header
403ForbiddenThe caller is authenticated but not allowed to do this
404Not FoundThe resource does not exist, or you do not want to reveal that it does
405Method Not AllowedThe URL exists but not for this method; include an Allow header
409ConflictThe request conflicts with current state, such as a duplicate or version clash
410GoneThe resource was removed permanently and will not return
415Unsupported Media TypeThe Content-Type of the body is not accepted
422Unprocessable ContentThe body is well-formed but fails validation
429Too Many RequestsThe client hit a rate limit; include Retry-After when possible

The difference between 401 and 403 trips up many APIs. Use 401 when the server does not know who the caller is, so logging in or refreshing a token could help. Use 403 when the server knows exactly who the caller is and the answer is still no.

For validation problems, return 400 or 422 with a body that names each invalid field and explains why, so the client can show useful messages instead of a generic failure.

5xx: server errors

CodeNameWhen to use it
500Internal Server ErrorAn unexpected failure on the server
502Bad GatewayA proxy or gateway received an invalid response from the upstream service
503Service UnavailableTemporary overload or maintenance; include Retry-After when known
504Gateway TimeoutA proxy or gateway waited too long for the upstream service

A 5xx response should never expose stack traces or internal details to the public. Log the details on the server, return a short message with a request or correlation ID, and let clients retry idempotent requests with exponential backoff.

Designing error responses

A good status code tells a client what kind of failure happened; a good error body tells it why. RFC 9457, Problem Details for HTTP APIs, defines a standard JSON shape served as application/problem+json, so clients can handle errors from different APIs in the same way.

{
  "type": "https://example.com/problems/insufficient-credit",
  "title": "Insufficient credit",
  "status": 403,
  "detail": "Your balance is 30, but the order costs 50.",
  "instance": "/orders/12345"
}

Whatever format you choose, keep it consistent across every endpoint, include a stable machine-readable error code alongside the human-readable message, and never leak internal details such as stack traces or database errors.

Status codes and SEO

Search engines read status codes to decide what to index. Use 301 or 308 when a page moves permanently, so ranking signals transfer to the new URL, and keep redirect chains short, ideally a single hop.

Pages that no longer exist should return a real 404 or 410. A page that displays "not found" but returns 200, known as a soft 404, wastes crawl budget and can keep dead pages in search results. Temporary maintenance should return 503 with Retry-After, which tells crawlers to come back later instead of dropping pages from the index.

Checking status codes yourself

On the command line, curl -I shows the status line and headers, and -L follows redirects so you can see a whole chain.

curl -I https://example.com/old-page
curl -sIL https://example.com/old-page | grep -i -E "^HTTP|^location"

The UtiliStudio REST Client shows the status, timing, and headers for any request you send, and the HTTP status checker in SEO Utilities summarizes response headers for a page.

Try it: REST ClientSend HTTP requests, inspect responses and test APIs without leaving UtiliStudio.
Open REST Client