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.
| Class | Meaning | Client should |
|---|---|---|
| 1xx | Informational | Keep waiting for the final response |
| 2xx | Success | Use the response |
| 3xx | Redirection | Follow the new location or use a cached copy |
| 4xx | Client error | Fix the request before retrying |
| 5xx | Server error | Retry 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
| Code | Name | When to use it |
|---|---|---|
| 200 | OK | The request succeeded and the body contains the result |
| 201 | Created | A new resource was created; include a Location header pointing to it |
| 202 | Accepted | The work was queued and will finish later; return a way to check status |
| 204 | No Content | The 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
| Code | Name | When to use it |
|---|---|---|
| 301 | Moved Permanently | A URL has changed for good, such as after a site restructure |
| 302 | Found | A temporary redirect; the original URL remains canonical |
| 303 | See Other | After a form POST, send the browser to a result page with GET |
| 304 | Not Modified | The cached copy is still valid; no body is sent |
| 307 | Temporary Redirect | Like 302, but the method and body must be kept |
| 308 | Permanent Redirect | Like 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
| Code | Name | When to use it |
|---|---|---|
| 400 | Bad Request | The request is malformed, such as invalid JSON or a missing parameter |
| 401 | Unauthorized | Authentication is missing or invalid; include a WWW-Authenticate header |
| 403 | Forbidden | The caller is authenticated but not allowed to do this |
| 404 | Not Found | The resource does not exist, or you do not want to reveal that it does |
| 405 | Method Not Allowed | The URL exists but not for this method; include an Allow header |
| 409 | Conflict | The request conflicts with current state, such as a duplicate or version clash |
| 410 | Gone | The resource was removed permanently and will not return |
| 415 | Unsupported Media Type | The Content-Type of the body is not accepted |
| 422 | Unprocessable Content | The body is well-formed but fails validation |
| 429 | Too Many Requests | The 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
| Code | Name | When to use it |
|---|---|---|
| 500 | Internal Server Error | An unexpected failure on the server |
| 502 | Bad Gateway | A proxy or gateway received an invalid response from the upstream service |
| 503 | Service Unavailable | Temporary overload or maintenance; include Retry-After when known |
| 504 | Gateway Timeout | A 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.
Keep learning
- CORS errors explained, and how to fix them
- JWT explained: structure, claims, and security pitfalls
- SEO Utilities: Audit meta tags, preview search engine snippets, and analyze page metadata.