HTTP-Fehlercodes, die Sie tatsächlich treffen — 301 vs. 302, 401 vs. 403 und das 5xx-Feld
Kein Wörterbuch. Ein fokussierter Leitfaden zu HTTP-Statuscodes, die echte Produktionsprobleme verursachen – die falsche Umleitung, die nun dauerhaft gespeichert ist, das Verwechseln von 401/403, das Ihre Authentifizierungslösung enthüllt, 429 ohne Retry-After und das Verwechseln von 503 und 5-04, das Sie die falsche Schicht bei der Fehlerbehebung anvisiert.
You’ve shipped a redirect and it was the wrong type. Now every browser that hit it before you noticed has cached it permanently, and the only way to fix it is to wait for cache expiry or beg users to clear their browser history. That’s a 301.
This isn’t a status code dictionary — there are plenty of those. This is the guide you want after you’ve read the table and still shipped the wrong code. We’re going through the ones that cause actual bugs: permanent caches when you wanted temporary ones, auth errors that leak information, rate limit responses you’re not handling correctly, and gateway timeouts that point to the wrong layer.
301 vs 302 vs 307 vs 308: The Redirect Matrix
The mistake everyone makes at least once: you use 301 Moved Permanently while testing a URL restructure. It works. You move on. Then you restructure again. And now a chunk of your users — anyone who visited before the second change — are being sent to the old destination forever, cached right there in their browser.
301 is permanent and it caches aggressively. Browsers set the cached redirect’s TTL to essentially infinity unless you’ve set a Cache-Control header. There’s no programmatic way to expire it from the user’s browser. If you’re still figuring out your URL structure, use 302.
The method-preservation problem is a different beast. When a browser follows a 301 or 302, it may (and in practice, almost always does) downgrade a POST to a GET on the redirect. This is technically a violation of the original HTTP/1.0 spec, but browsers normalized it so long ago that RFC 7231 acknowledges it as standard behavior. If you’re redirecting a POST — say, after a form submission — and you expect the method to be preserved, you need 307 or 308.
| Code | Permanent? | Preserves Method? | Use it for |
|---|---|---|---|
| 301 | Ja | No (POST → GET) | Permanent URL moves where GET is fine after redirect |
| 302 | NEIN | No (POST → GET) | Temporary redirects, feature flags, A/B tests |
| 307 | NEIN | Ja | Temporary redirects where method must be preserved |
| 308 | Ja | Ja | Permanent redirects where method must be preserved |
308 support is solid now — Chrome, Firefox, Safari, and Edge all handle it correctly. The caveat: some older API clients and HTTP libraries still don’t follow 308 correctly, so if you’re redirecting machine-to-machine traffic and control neither the client nor its HTTP library, test it explicitly.
401 vs 403: Auth vs Authz
401 means “you haven’t identified yourself.” The spec requires it to include a WWW-Authenticate header telling the client how to authenticate. It’s the correct code when there’s no valid session, no token, or the token has expired.
403 means “I know who you are and the answer is no.” The user is authenticated but lacks the permission. Returning 403 on an expired token is wrong — that’s a 401. Returning 401 on a valid-but-insufficient-permissions request is also wrong, and worse, it confuses your auth debugging: you’ll look for missing credentials when the real problem is role assignment.
The security argument for returning 404 instead of 403 is real. If your API returns 403 on GET /admin/users, you’ve just told any attacker that the endpoint exists and they’d need higher privileges to reach it. Returning 404 instead leaks no information about the existence of the resource. It’s a judgment call: 403 is technically correct and easier to debug; 404 is the secure choice for sensitive endpoints where enumerability matters.
429: Rate Limiting Is Useless Without Retry-After
A 429 response without a Retry-After header is a black box. The client knows it’s been rate limited. It doesn’t know if it should wait 100ms or 24 hours. Most client implementations either retry immediately (hammering your rate limiter) or give up entirely.
Retry-After takes either an integer (seconds to wait) or an HTTP-date (when to retry):
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1749254400
Der X-RateLimit-* headers aren’t in any RFC — they’re a de facto standard from GitHub’s API that everyone copied. Include them alongside Retry-After. The trio of Limit/Remaining/Reset tells the client where it stands before it hits the wall, which is more useful than just knowing it hit the wall.
One thing worth calling out: Retry-After is also valid on 503 responses, and it means the same thing — “come back in this many seconds.” If your service is temporarily unavailable for a maintenance window, send a 503 with Retry-After instead of just dropping connections.
503 vs 504: Where Is the Failure?
These two look similar but point to completely different failure layers, and mixing them up sends you debugging in the wrong direction.
503 Service Unavailable means the server you reached can’t handle the request right now — it’s overloaded, in maintenance mode, or a backend dependency is down. The server itself responded; it’s just refusing the work.
504 Gateway Timeout means a proxy or gateway (your load balancer, nginx, API gateway, CDN) tried to reach an upstream server and got no response in time. The server you hit is alive. The thing behind it isn’t responding.
In practice: if you’re behind nginx and your application server (Node, Rails, Django, whatever) crashes, you get 502 Bad Gateway — nginx got a response but it was garbage. If the application server stops responding entirely, you get 504. If you intentionally return an error from the application layer, you get 503. The distinction matters when you’re triaging: 504 means look at the upstream service, your timeout configuration, and your network. 503 means look at the application itself.
422 vs 400: Validation Errors Have Their Own Code
400 Bad Request is for requests the server can’t parse — malformed JSON, invalid query string syntax, missing required headers. It’s a structural problem.
422 Unprocessable Entity is for requests the server understood but can’t act on because the data fails validation — a date range where start is after end, an email field with an invalid address, a quantity field that’s negative. The request was syntactically fine. The semantics were wrong.
Most APIs collapse both into 400 and bury the validation details in the response body. That works, but it makes client-side error handling harder: clients have to parse the body to know whether it’s a parsing failure or a validation failure. Using 422 for validation errors lets clients key on the status code instead. Rails has done this correctly since forever; the JSON:API spec specifies 422 for validation errors too.
The one nuance: 422 is defined in WebDAV (RFC 4918), not the core HTTP spec. In practice this doesn’t matter — every HTTP client and server handles it fine — but you’ll occasionally run into a pedant who insists you should use 400 instead. They’re not wrong, exactly. But 422 is more specific and widely understood.
204 vs 200 With an Empty Body: One Is Correct for DELETE
When a DELETE succeeds, the correct response is 204 No Content — not 200 with an empty body, and not 200 with {"success": true}.
204 is the explicit signal that “this succeeded and there is intentionally no response body.” A 200 with an empty body is technically valid, but it’s ambiguous — clients don’t know if the body was supposed to be there and got lost, or if the absence is intentional. 204 removes the ambiguity.
The same logic applies to PUT and PATCH when you don’t return the updated resource. If your API returns the updated object after a PATCH, use 200. If it doesn’t, use 204. Don’t return 200 with a body of {} oder null — that’s 204 wearing a costume.
One gotcha: 204 must not include a message body per RFC 9110. If you return 204 and accidentally include a body (some frameworks will let you), some HTTP clients will handle it gracefully, others won’t. Strip the body in your response handler, not just in your intent.
Quick Reference: The Codes That Bite and Why
| Code | Bedeutung | Common Mistake | Fix |
|---|---|---|---|
| 301 | Permanent redirect | Using during testing — now it’s cached forever | Use 302 until the redirect is confirmed permanent |
| 302 | Temporary redirect | POST gets downgraded to GET on follow | Use 307 if the method must be preserved |
| 307 | Temporary redirect (method-safe) | Confused with 302 | Use when redirecting POST/PUT/PATCH temporarily |
| 308 | Permanent redirect (method-safe) | Skipped in favor of 301 out of habit | Use instead of 301 when method matters |
| 400 | Bad request (parse error) | Used for validation errors too | Use 422 for semantic validation failures |
| 401 | Unauthenticated | Returned when user lacks permission (should be 403) | Return 401 only when credentials are missing or expired |
| 403 | Verbotene | Returned instead of 404 for security-sensitive endpoints | Consider 404 when hiding endpoint existence matters |
| 422 | Unprocessable entity | Collapsed into 400 | Use for validation failures where the body was parseable |
| 429 | Rate limited | No Retry-After header | Always include Retry-After + X-RateLimit-* headers |
| 503 | Service unavailable | Confused with 504 | Use when the reached server can’t handle the request |
| 504 | Gateway timeout | Confused with 503 | Investigate the upstream service, not the gateway |
| 204 | No content | Returning 200 with empty body instead | Use 204 for successful DELETE/PUT/PATCH with no response body |
If you need to quickly look up any status code while debugging, IO Tools has an HTTP Status Codes Lookup that covers the full range with descriptions and notes on when to use each one.
The Pattern Behind All of These
Most of these mistakes come from the same place: treating status codes as loose categories (“4xx is client error, 5xx is server error, done”) rather than as a protocol with specific semantics. The semantics matter because clients — browsers, HTTP libraries, CDNs, monitoring tools — actually key off them. A CDN won’t cache a 200 and a 204 the same way. An HTTP client won’t retry a 400 and a 503 with the same logic. A browser won’t cache a 302 and a 301 with the same TTL.
Use the right code. The overhead is zero. The debugging benefit when something breaks is not.
Erweiterungen installieren
IO-Tools zu Ihrem Lieblingsbrowser hinzufügen für sofortigen Zugriff und schnellere Suche
恵 Die Anzeigetafel ist eingetroffen!
Anzeigetafel ist eine unterhaltsame Möglichkeit, Ihre Spiele zu verfolgen. Alle Daten werden in Ihrem Browser gespeichert. Weitere Funktionen folgen in Kürze!
Unverzichtbare Tools
Alle Neuheiten
AlleAktualisieren: Unser neuestes Werkzeug wurde am 7. Juni 2026 hinzugefügt
