HTTP Status Codes Explained: The Ones That Actually Get Confused
Published 4/29/2026 · 8 min read · Developer tools
Daniel Okonkwo — Front-end developer and tech writer at OneKitly
Web performance · File formats
Checked against 2 sources
The first digit is the part every client, cache and crawler reads: 1xx informational, 2xx success, 3xx redirect, 4xx the request was at fault, 5xx the server was. Within those, the distinctions that change behaviour are these. 308 is 301 with a guarantee that the method and body survive the redirect, and 307 is 302 with the same guarantee — older clients turn a redirected POST into a GET on 301 and 302, which is exactly why 307 and 308 exist. 401 means the request carried no valid credentials and must come with a WWW-Authenticate header; 403 means the credentials were fine and the action is still refused, so logging in again will not help. 404 says there is nothing here without committing to why; 410 says the resource was deliberately removed and is not coming back, and is cacheable by default. On 429 and 503, Retry-After is an estimate the server publishes as a number of seconds or an HTTP date — it is guidance for well-behaved clients, not a promise that the service returns on time.
301 against 308, 302 against 307, 401 against 403, 404 against 410 — plus what Retry-After on a 429 or a 503 actually promises. The pairs where picking the wrong code changes behaviour, not just wording.
The first digit is the only part some clients read
RFC 9110 requires a client to understand the class of a status code even when it does not recognise the code itself, and to treat anything unfamiliar as the x00 of its class. A client that has never heard of 451 handles it as a 400; an unknown 599 is handled as a 500. That rule is what makes the space extensible, and it also means the class carries almost all of the behaviour: caches decide storability from it, proxies decide retries from it, and crawlers decide indexing from it, mostly before anything in your response body is parsed.
The practical consequence is that returning 200 with an error described in the body puts a failure somewhere nothing else can see it. Your uptime check reports green, your CDN caches the error page, and a search engine indexes it as a real document. This is the soft 404 problem in general form, and it is worth auditing for, because the fix is a one-line change to a status code rather than anything architectural.
Redirects: which ones keep your POST
The four-code redirect grid is really two questions crossed: permanent or temporary, and does the method survive. 301 and 308 are permanent, 302 and 307 are temporary; 307 and 308 are the two that forbid the client from changing the request. MDN is blunt about why the newer pair exists: 301 already required the method and body to stay unchanged, but that requirement was incorrectly handled by older clients, which switched to GET. Nobody could fix the installed base, so two new codes were minted with the guarantee written into their definition.
So the choice follows from the traffic. For a page that only ever receives GET, 301 is fine and is what search engines are most used to. For an API path, a form target or anything that can receive POST, PUT or DELETE, use 308 and the request arrives intact. One caution about permanence: browsers cache a 301 aggressively and some keep it far longer than you expect, so a 301 issued during an experiment can outlive the experiment on machines you have no access to. Ship 302 or 307 while you are still deciding.
The errors most often returned wrong
401 and 403 are the pair that costs the most support time. 401 means the request lacked valid authentication credentials, and MDN notes that it is sent with a WWW-Authenticate header describing the scheme the server expects — it is an invitation to try again with credentials. 403 is the opposite situation: the credentials are perfectly valid, and the client still does not have permission for this action. Returning 401 to a signed-in user tells their client to re-authenticate, which will produce exactly the same refusal, and tells the user that their login is broken when it is not.
404 and 410 differ only in confidence, and that is the point. MDN's guidance is explicit: if the server owner does not know whether the condition is temporary or permanent, use 404. Use 410 when you removed the thing on purpose and it is not coming back — it is cacheable by default and tells crawlers not to keep asking. Retry-After, meanwhile, is a header that carries either a delay in seconds or an HTTP date; on a 503 it estimates how long the service will be unavailable and on a 429 it says how long to wait before a new request. It is guidance, not a contract, and MDN notes that support across clients is uneven — but Googlebot honours it, which alone makes it worth setting during planned maintenance.
| Code | Name | What it commits you to | Where it goes wrong |
|---|---|---|---|
| 200 | OK | The request succeeded and the body is the result | Returned with an error message inside the body, which hides the failure from caches, monitoring and crawlers |
| 301 | Moved Permanently | This URL is replaced for good; update your links | Used while still deciding — browsers cache it hard, and older clients turn a redirected POST into a GET |
| 302 | Found | Go here for now; keep using the original URL | Used for a permanent move, so the old URL keeps absorbing the ranking signals |
| 304 | Not Modified | Your cached copy is still valid; there is no body | Sent outside a conditional request, or sent with a body that clients are entitled to discard |
| 307 | Temporary Redirect | Like 302, but the method and body must not be changed | Rarely reached for, so temporary redirects silently break POST-based flows |
| 308 | Permanent Redirect | Like 301, but the method and body must not be changed | Overlooked for API endpoints, where a 301 quietly downgrades a POST to a GET |
| 401 | Unauthorized | No valid credentials were presented; a WWW-Authenticate header says what is expected | Returned to a user who is already signed in but lacks the right — that case is 403 |
| 403 | Forbidden | The identity is accepted and the action is still refused | Used as a catch-all, which invites clients to retry authentication that will never help |
| 404 | Not Found | There is nothing at this URL, and the server does not say whether that is permanent | Replaced by a friendly page served with 200, the soft 404 that keeps dead URLs indexed |
| 410 | Gone | Deliberately removed and not coming back; cacheable by default | Almost never used, even when the removal was intentional and 404 understates it |
| 429 | Too Many Requests | A rate limit was hit; Retry-After says how long to wait before a new request | Sent without Retry-After, leaving clients to guess and hammer the endpoint |
| 500 | Internal Server Error | The server failed and has nothing more specific to say | Returned for a bad request from the client, which belongs in the 400 range instead |
| 502 | Bad Gateway | A proxy received an invalid response from the server it forwarded to | Debugged in the application, when the fault sits between the proxy and the upstream |
| 503 | Service Unavailable | Temporarily unable to serve; Retry-After estimates how long the outage will last | Replaced by a 500 during planned maintenance, so crawlers treat the outage as a real failure |
Frequently asked questions
- Should a site migration use 301 or 308?
- For pages that are only ever fetched with GET, 301 is the safe default and the one every crawler and proxy has handled for decades. Reach for 308 on anything that can receive a POST, PUT or DELETE — form endpoints, API routes, webhook receivers — because that is where a client silently downgrading the method turns a redirect into a lost request body. There is no reason you cannot use both on the same migration, chosen per route.
- What should an API return when validation fails?
- Use 400 Bad Request when the request itself is malformed and the server cannot parse it — broken JSON, a missing required header. Use 422 Unprocessable Content when the syntax is fine but the content breaks your rules, such as a well-formed body with an end date before its start date. What you should not return is 500, which blames the server for a client mistake and pollutes your error budget, or 200 with the problem described in the body, which hides the failure from every layer between you and the caller.
- Do 404s hurt search rankings?
- A 404 is a valid, honest answer and a normal part of any site that has existed for a while; a page that no longer exists should say so. The damage comes from the substitutes. A soft 404 — a friendly page served with status 200 — keeps dead URLs in the index and spends crawl budget on nothing. A blanket redirect of every missing page to the homepage is the same mistake wearing a 301. If the removal was deliberate and permanent, 410 states it plainly.
Articles you may find interesting
All guides →Related tools
Sources
Spotted a mistake in this article?