NetLabToolsNetLabTools

HTTP status codes explained: when to use 401 vs 403 vs 422

The most important HTTP status codes explained clearly — including rules of thumb for 401, 403 and 422.

7 min read

HTTP status codes are the shortest form of API documentation — when used correctly. In practice you still see 200 for errors, 500 for validation issues, and 403 for unauthenticated users. Here's the clean version.

2xx – Success

  • 200 OK – Standard success response, usually with a body.
  • 201 Created – A new resource was created. The Location header should point to it.
  • 204 No Content – Success, but intentionally no body. Ideal for DELETE or PUT when the client already knows the resulting state.

3xx – Redirects

The difference between 301 and 302 is one of the most common SEO traps.

  • 301 Moved Permanently – Permanent. Browsers and search engines cache it aggressively.
  • 302 Found – Temporary. Per spec the method may change (POST → GET), and browsers do.
  • 307 Temporary Redirect – Temporary, but method is preserved. Safer than 302 for POST.
  • 308 Permanent Redirect – Like 301, but method preserved. The modern choice.

4xx – Client errors

This is where most of the confusion lives. 400 is generic (malformed request, e.g. broken JSON), 404means "resource doesn't exist or you may not see it". The interesting trio is 401, 403, and 422:

CodeMeaningWhen to useExample
401Unauthorized – not authenticatedMissing token, expired token, wrong credentialsAPI call without an Authorization header
403Forbidden – authenticated, but not allowedUser is logged in but lacks permissionRegular user hits an admin endpoint
422Unprocessable Entity – validation failedRequest format is valid, but content is logically wrongEmail syntax is fine but already taken

Other relevant 4xx codes:

  • 409 Conflict – State conflict, e.g. concurrent edit or duplicate username.
  • 429 Too Many Requests – Rate limit hit. Send a Retry-After header.

5xx – Server errors

  • 500 Internal Server Error – Catch-all for unexpected server problems. Log everything that lands here.
  • 502 Bad Gateway – Your reverse proxy (nginx, Cloudflare) got a broken or no response from the backend.
  • 503 Service Unavailable – Server is intentionally offline (maintenance, overload). Combine with Retry-After.
  • 504 Gateway Timeout – Backend is too slow to respond.

Rules of thumb

  1. Never 200 for errors — not even with { "error": "..." } in the body. Clients and monitoring rely on the status code.
  2. 401 vs 403:the question is not "may you do this?" but "who are you?". Don't know you (or invalid token)? → 401. Know who you are, but you can't? → 403.
  3. 404 instead of 403 for private content — that way you don't even leak the resource's existence. GitHub does this for private repos.
  4. 422 instead of 400 for semantic validation errors (missing required field, wrong format). Use 400 for structurally broken requests (invalid JSON).
  5. 201 with a Location header when you create something. It's REST-standard and helps clients and tooling.
  6. Use 204 when the body is empty — semantically clearer than 200 with an empty body.

Consistency beats perfection: if your team agrees on a clear rule and sticks to it, that's worth more than the theoretically "perfect" choice in every individual case.