The first digit of an HTTP status code tells you who to look at: 2xx means it worked, 3xx means go somewhere else, 4xx means the request was wrong, and 5xx means the server failed. Within those, the codes people actually run into are 200, 201, 204, 301, 302, 304, 400, 401, 403, 404, 405, 409, 422, 429, 500, 502, 503 and 504, each explained below with what to do about it.
The definitions come from RFC 9110, the current HTTP semantics standard, except 429, which is in RFC 6585.
How to see the status code
In a browser, open developer tools (F12), go to the Network tab and reload; the Status column shows the code for every request. From a terminal, curl -I prints just the response headers:
curl -I http://github.com
Example output:
HTTP/1.1 301 Moved Permanently
Content-Length: 0
Location: https://github.com/
To print only the number, which is handy in scripts: curl -s -o /dev/null -w "%{http_code}\n" https://example.com/.
The five classes
| Class | Meaning (RFC 9110) | Whose problem |
|---|---|---|
| 1xx Informational | Request received, continuing process | Nobody; you rarely see these |
| 2xx Successful | The request was successfully received, understood, and accepted | Nobody |
| 3xx Redirection | Further action needs to be taken to complete the request | Usually handled automatically |
| 4xx Client Error | The request contains bad syntax or cannot be fulfilled | The caller: fix the request |
| 5xx Server Error | The server failed to fulfil an apparently valid request | The server or something in front of it |
2xx: success
- 200 OK: "the request has succeeded". What the body contains depends on the method: the resource for a
GET, the result of the action for aPOST. - 201 Created: the request created a new resource. The server identifies it with a
Locationheader, so a client that justPOSTed a new order should read that header to find the order's URL. - 204 No Content: success, with nothing to send back. Common for
DELETEand forPUTupdates. A 204 response has no body, so client code that always callsresponse.json()will throw on it.
3xx: redirects and caching
- 301 Moved Permanently: the resource has a new permanent URL, given in
Location. Use it when you move a page for good (HTTP to HTTPS, a renamed URL). - 302 Found: the resource is temporarily somewhere else; keep using the original URL in future. RFC 9110 notes that "for historical reasons" a client may change a
POSTinto aGETwhen following a 301 or 302, and MDN says browsers following the Fetch standard do this for 302. If the method must be kept, use 307 (temporary) or 308 (permanent), which forbid changing it. - 304 Not Modified: not an error. The client asked "has this changed since my cached copy?" (with
If-None-MatchorIf-Modified-Since), and the answer is no, so the server sends no body and the client uses its cache. Seeing many 304s in the Network tab means caching is working.
4xx: the request is the problem
| Code | Meaning | What to check |
|---|---|---|
| 400 Bad Request | The server can't process the request because of a client error, such as malformed syntax | Invalid JSON, a wrong Content-Type, missing required fields, a malformed query string |
| 401 Unauthorized | No valid authentication credentials | Missing or expired token, wrong API key, Authorization header not sent |
| 403 Forbidden | Understood, but refused | Your account or token lacks permission for this resource or action |
| 404 Not Found | No current representation found, or the server won't say it exists | Typo in the path, wrong base URL or API version, trailing slash differences |
| 405 Method Not Allowed | The URL exists but doesn't support this method | Sending POST where only GET works; the Allow header lists what is supported |
| 409 Conflict | Conflicts with the current state of the resource | Duplicate unique value, editing a stale version; re-fetch and retry |
| 422 Unprocessable Content | Syntax is fine, but the content can't be processed | Field validation failures in well-formed JSON |
| 429 Too Many Requests | Too many requests in a given time (rate limiting) | Slow down; honour the Retry-After header if present |
Two details from the RFC that save debugging time. A server that sends 405 "MUST generate an Allow header" listing the methods the URL supports, so read it. And a server that wants to hide that a forbidden resource exists may send 404 instead of 403, so a 404 on a URL you know exists can be a permissions problem.
401 vs 403
The names are misleading. 401 is called "Unauthorized" but means unauthenticated, as MDN puts it: the server doesn't know who you are. RFC 9110 requires a 401 to include a WWW-Authenticate header describing how to authenticate. You can trigger a 401 by calling GitHub's API without a token:
curl -I https://api.github.com/user
HTTP/1.1 401 Unauthorized
When this was checked for this article, GitHub's response carried no WWW-Authenticate header at all, so don't rely on every API following that part of the standard; read the API's own documentation for how to authenticate.
403 means the server knows the request and refuses it. If credentials were sent, the RFC says the server "considers them insufficient to grant access", and the client should not retry with the same ones. The practical test: would logging in, or refreshing the token, fix it? Then it is a 401. Would it only be fixed by someone granting you more permissions? Then it is a 403.
5xx: the server is the problem
- 500 Internal Server Error: "an unexpected condition" stopped the server. This is the generic crash code, usually an unhandled exception. The useful information is in the server's logs, not in the response; how to read a stack trace covers what to look for there.
- 502 Bad Gateway: a proxy or gateway got "an invalid response" from the server behind it.
- 503 Service Unavailable: temporary overload or maintenance. The server may send
Retry-Afterto say when to try again. - 504 Gateway Timeout: a proxy or gateway "did not receive a timely response" from the server behind it.
502 vs 504
Both mean there are at least two servers involved: something in front (a reverse proxy such as nginx, a load balancer, a CDN, an API gateway) and the application behind it. The front server is the one reporting the error, about the one behind it.
- 502: the application answered, but with something the proxy couldn't use. MDN's distinction is that if the proxy received no HTTP response at all from the origin, it sends a 504 instead.
- 504: the application didn't answer within the proxy's time limit. Look for slow database queries, a hung process, or a request that does too much work synchronously.
In both cases, check the application's own logs first, then the proxy's error log; the proxy usually records why it gave up on the upstream.
Which codes to retry
Retrying a 4xx unchanged gets the same answer, with one exception: 429, after waiting. Among server errors, 503 is defined as temporary and may come with Retry-After; 502 and 504 can also clear once the upstream recovers. Leave an increasing delay between attempts rather than retrying in a tight loop. Before retrying a POST automatically, make sure repeating it can't create a duplicate (a second order, a second payment).
If a browser request fails with no status code at all and the console mentions CORS, the server may well have replied with 200; the browser just won't show it to your script. See CORS errors explained.
Frequently asked questions
- What is the difference between 401 and 403?
- 401 means the server doesn't know who you are: the request had no valid credentials, and the server must send a WWW-Authenticate header saying how to authenticate. 403 means the server understood the request and refuses it; if you sent credentials, they are not sufficient. Logging in can fix a 401; it won't fix a 403 unless you log in as someone with more permissions.
- What is the difference between 502 and 504?
- Both come from a proxy, load balancer or gateway in front of the real server. 502 Bad Gateway means it received an invalid response from the server behind it. 504 Gateway Timeout means it received no response in time. A 504 points at something slow or hung; a 502 points at something that answered badly.
- Should an API return 400 or 422 for validation errors?
- RFC 9110 defines 400 for requests the server can't process because of a client error such as malformed syntax, and 422 for content whose syntax is correct but whose instructions can't be processed. Many APIs use 422 for well-formed JSON that fails validation and 400 for JSON that doesn't parse. Either is acceptable if the API is consistent and documents it.
- Is 404 always permanent?
- No. RFC 9110 says a 404 doesn't indicate whether the missing resource is gone temporarily or permanently. A server that knows a resource is permanently removed should send 410 Gone instead.