What you'll learn
Quick Answer
HTTP status codes are grouped by their first digit: 2xx means success, 3xx means redirection, 4xx means the client sent something wrong, and 5xx means the server failed. The distinction that matters most is 4xx versus 5xx — a 4xx says fix your request, a 5xx says the request was fine but the server broke. The most-confused pairs are 401 (not authenticated) versus 403 (authenticated but not allowed), and 301 (permanent, cached aggressively) versus 302 (temporary).
The Five Families
You do not need to memorise the full list. The first digit carries most of the meaning.
- 1xx — informational. Rare in application code.
101 Switching Protocolsappears when a connection upgrades to WebSockets. - 2xx — success. The request was received, understood and accepted.
- 3xx — redirection. The resource is somewhere else; follow the
Locationheader. - 4xx — client error. Something about the request was wrong. Retrying it unchanged will fail again.
- 5xx — server error. The request was reasonable but the server could not fulfil it. Retrying may well succeed.
That 4xx versus 5xx split is the one to internalise. It answers "whose fault is this?", which decides whether you debug your client or your server — and it also decides whether a retry is sensible. Retrying a 400 is pointless; retrying a 503 with backoff is exactly right.
It matters for SEO too. Google treats a 404 as "this is gone, drop it" and a 503 as "come back later, keep the page". Returning the wrong one during maintenance can quietly remove pages from the index.
Success Codes Worth Distinguishing
200 OK — the general success. The response body carries the result.
201 Created — a new resource was created. Correct for a POST that adds a record, and it should include a Location header pointing to the new thing. Returning a bare 200 works but tells the client less.
204 No Content — succeeded, and there is deliberately nothing to send back. The right answer for a DELETE, or a PUT where the client already knows the result. A 204 must not have a body; some clients will error if you send one anyway.
202 Accepted — the request is valid and queued, but not finished. The honest code for asynchronous work such as "generate this report", where pretending it completed with a 200 would be a lie.
A common API mistake is returning 200 OK with {"error": "not found"} in the body. Monitoring, caches and client libraries all read the status code, so an error dressed as a success is invisible to every one of them.
Redirects: 301 vs 302 Matters More Than You Think
301 Moved Permanently tells clients and search engines the resource has moved for good. Browsers cache it aggressively — often indefinitely — and search engines transfer ranking signals to the new URL.
That caching is the danger. If you issue a 301 by mistake, visitors who received it may keep being redirected even after you fix the server, because their browser never asks again. Clearing that requires the user to clear their cache. Test redirects with a 302 first, and promote to 301 once you are certain.
302 Found is temporary. Nothing is cached long-term and search engines keep the original URL indexed. Correct for "log in first, we will send you back" flows.
307 and 308 are the strict versions. The older codes allowed clients to change a POST into a GET when following the redirect, which surprised people; 307 (temporary) and 308 (permanent) guarantee the method and body are preserved. Prefer these for APIs.
304 Not Modified is the caching workhorse. The client asks "has this changed since the version I hold?" and the server replies 304 with no body. It looks like an error in the network tab but is the fastest possible successful response.
Client Errors, Including the Pair Everyone Confuses
400 Bad Request — the request itself is malformed: broken JSON, a missing required field, a wrong data type. The server could not even interpret it.
401 Unauthorized — you are not authenticated. No credentials, or invalid ones. The name is historically wrong; read it as "unauthenticated". The correct response to "who are you?"
403 Forbidden — you are authenticated, and you still may not do this. The server knows who you are and is refusing anyway. The correct response to "you are not allowed".
The rule: 401 means log in; 403 means logging in will not help. A normal user hitting an admin endpoint should get 403, not 401 — sending 401 tells them to authenticate, which they already have.
404 Not Found — no such resource. Sometimes used deliberately in place of 403 to avoid revealing that something exists at all.
405 Method Not Allowed — the URL exists but not for that verb, such as POSTing to a read-only endpoint.
409 Conflict — the request clashes with current state, like registering an email that already exists.
422 Unprocessable Entity — the syntax is valid but the content fails validation. Well-formed JSON with an email field containing "banana". Many APIs use 400 for both; splitting them makes error handling clearer.
429 Too Many Requests — rate limited. Should include a Retry-After header, and a well-behaved client honours it rather than hammering.
Server Errors and What They Point To
500 Internal Server Error — the catch-all. Something threw an exception and nothing handled it. This means "look at your server logs", and it should never leak a stack trace to the client, since that hands an attacker your framework versions and file paths.
502 Bad Gateway — a proxy or load balancer got an invalid response from the server behind it. Usually your application crashed or was never listening; nginx returns 502 when it cannot get a sensible answer from the app.
503 Service Unavailable — temporarily unable to handle the request, through overload or maintenance. This is the correct code for planned downtime, and it should include Retry-After. Search engines treat it as "try again later" and keep the page indexed, which a 404 would not.
504 Gateway Timeout — a proxy waited for the upstream server and gave up. The application is alive but too slow, so look for slow queries or a blocked event loop rather than a crash.
The 502 versus 504 distinction saves real debugging time: 502 usually means dead, 504 usually means slow.
