كودات حالة HTTP التي تُلقي بسَلَمك — 301 مقابل 302، 401 مقابل 403، والمنطقة الخطرة من الكودات 5xx

نُشرت في

ليس قاموسًا. دليل مركّز على أكواد حالة HTTP التي تسبب أخطاء في البيئة الإنتاجية الحقيقية — التوجّه الخاطئ الذي تم تحميله بشكل دائم، خلط 401/403 الذي يُعرض معلومات التحقق من الهوية، 429 بدون Retry-After، وخلط 503 مقابل 504 الذي يُرسلك للتحقق من الطبقة الخاطئة.

HTTP Status Codes That Actually Bite You — 301 vs 302, 401 vs 403, and the 5xx Minefield 1
إعلان · حذف؟

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.

شفرةPermanent?Preserves Method?Use it for
301نعمNo (POST → GET)Permanent URL moves where GET is fine after redirect
302لاNo (POST → GET)Temporary redirects, feature flags, A/B tests
307لانعمTemporary redirects where method must be preserved
308نعمنعم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

ال 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 غير متوفر 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 تأخير في البوابة 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 طلب غير صحيح is for requests the server can’t parse — malformed JSON, invalid query string syntax, missing required headers. It’s a structural problem.

422 كيان غير قابل للتنفيذ 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 لا يوجد محتوى — 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 {} أو 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

شفرةمعنىCommon MistakeFix
301Permanent redirectUsing during testing — now it’s cached foreverUse 302 until the redirect is confirmed permanent
302Temporary redirectPOST gets downgraded to GET on followUse 307 if the method must be preserved
307Temporary redirect (method-safe)Confused with 302Use when redirecting POST/PUT/PATCH temporarily
308Permanent redirect (method-safe)Skipped in favor of 301 out of habitUse instead of 301 when method matters
400Bad request (parse error)Used for validation errors tooUse 422 for semantic validation failures
401UnauthenticatedReturned when user lacks permission (should be 403)Return 401 only when credentials are missing or expired
403حَرَامReturned instead of 404 for security-sensitive endpointsConsider 404 when hiding endpoint existence matters
422Unprocessable entityCollapsed into 400Use for validation failures where the body was parseable
429Rate limitedNo Retry-After headerAlways include Retry-After + X-RateLimit-* headers
503Service unavailableConfused with 504Use when the reached server can’t handle the request
504Gateway timeoutConfused with 503Investigate the upstream service, not the gateway
204No contentReturning 200 with empty body insteadUse 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.

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟