HTTP Status Codes Guide: What Each Code Really Means
Every HTTP response carries a three-digit status code, and that number is the server telling the client how things went in one glance. Returning the right one makes an API predictable: clients, caches, and browsers all change behavior based on it. Returning the wrong one causes bugs that are maddening to trace, because the body says one thing and the status says another.
This guide walks through the five classes, the codes that actually come up, and the pairs people mix up most.
The five classes
The first digit groups every code into a class:
- 1xx informational — the request was received and the process continues. Rare in everyday app code.
- 2xx success — the request succeeded.
- 3xx redirection — further action is needed, usually following a different URL.
- 4xx client error — the request was malformed or not allowed. The caller should change something.
- 5xx server error — the server failed to fulfill a valid request. The caller did nothing wrong.
That 4xx vs 5xx split is the one to internalize. A 4xx says the problem is on the caller side, a 5xx says the problem is on yours. Returning a 500 for bad user input, or a 400 for a crashed database, sends monitoring and clients down the wrong path.
Success codes
- 200 OK — the standard success. A GET returns the resource, and the body carries data.
- 201 Created — a new resource was created, typically after a POST. Include a Location header pointing to the new resource.
- 204 No Content — success with no body to return. Common for DELETE, or a PUT where the client already has the data. Do not send a body with a 204.
Redirection codes
- 301 Moved Permanently — the resource has a new permanent URL. Browsers and search engines update their records, and clients may cache the redirect indefinitely.
- 302 Found — a temporary redirect. The original URL is still the right one; the client should keep using it next time.
- 304 Not Modified — used with caching. The client sent a validator such as an ETag, and the resource has not changed, so the server skips the body and the client reuses its cached copy.
Client error codes
- 400 Bad Request — the request is malformed in a way the server cannot process, such as broken JSON.
- 401 Unauthorized — authentication is missing or invalid. The server does not know who you are.
- 403 Forbidden — authentication succeeded, but you are not allowed to do this. The server knows who you are and the answer is still no.
- 404 Not Found — no resource exists at this URL.
- 409 Conflict — the request clashes with the current state, such as creating a user whose email already exists.
- 422 Unprocessable Entity — the syntax is fine but the content fails validation, such as an email field that is a valid string but not a valid address.
- 429 Too Many Requests — the client is rate limited. Pair it with a Retry-After header so the client knows when to try again.
Server error codes
- 500 Internal Server Error — a generic failure. Something threw an exception that was not handled. Avoid using it for things the caller could fix.
- 502 Bad Gateway — a server acting as a proxy got an invalid response from an upstream server.
- 503 Service Unavailable — the server is temporarily down, overloaded, or in maintenance. Like 429, a Retry-After header helps clients back off gracefully.
401 vs 403, the pair everyone confuses
These two sound similar and are routinely swapped. The distinction is about identity versus permission.
Return 401 when the server does not know who the caller is: no token, an expired token, or bad credentials. The implied message is authenticate and try again. Return 403 when the caller is authenticated but lacks rights for this action: a logged-in user hitting an admin-only endpoint. The implied message is you are known, and you still cannot do this.
A quick test: would logging in with different credentials fix it? If yes, it is 401. If the user is already correctly logged in and simply lacks permission, it is 403.
301 vs 302, the other common mix-up
The difference is permanence, and it has real consequences. A 301 tells clients and search engines the move is permanent. They cache it hard and update bookmarks and indexes, so reverting later is painful because clients keep following the old redirect from cache.
A 302 says the move is temporary, so clients keep requesting the original URL. Reach for 302 during maintenance pages, A/B tests, or any redirect you intend to remove. Use 301 only when you are confident the change is forever, such as a retired domain. Using 301 by accident for a temporary redirect is a frequent and hard-to-undo mistake.
Choosing the right code
A short mental checklist covers most cases. Did it succeed with data? 200. Did it create something? 201. Succeed with nothing to return? 204. Is the caller unauthenticated? 401. Authenticated but not allowed? 403. Does the resource not exist? 404. Did validation fail? 422. Did your code break? 500. Getting these right turns your API responses into an accurate signal instead of a guessing game.
Related tools: