skip to content

questions

6

In HTTP, what is the difference between status 401 Unauthorized and status 403 Forbidden, and what header must a 401 response carry?

level: juniorimportance: must knowfreq 85%

answer

  1. 401 = unauthenticated, 403 = unauthorized
  2. 401 MUST carry WWW-Authenticate challenge
  3. Expired token → 401, not 403
  4. 403 confirms existence → 404 to hide
  5. Clients refresh on 401, give up on 403

basics

~20 s

401 means authentication is missing or invalid — the server asks "who are you?" and must send a WWW-Authenticate header. 403 means the identity is known (or irrelevant) and the request is still refused; retrying with credentials will not help.

solid answer

~50 s

**401 Unauthorized** really means *unauthenticated*: no credentials were sent, or the ones sent could not be validated (missing, malformed, expired, bad signature). RFC 9110 requires a 401 to include a `WWW-Authenticate` header naming at least one challenge scheme, e.g. `WWW-Authenticate: Bearer realm="api", error="invalid_token"`. It is an invitation to retry with credentials. **403 Forbidden** means the server understood the request and refuses to authorize it. Re-authenticating will not change the outcome: the caller lacks the role or scope, crosses a tenant boundary, or is blocked by policy (IP allowlist, licence, disabled account). 403 is also correct for an anonymous request to something that is simply never public. Rule of thumb: **401 = fix your credentials; 403 = your credentials are fine and still not enough.** Returning 403 for an expired token is a classic bug — clients key their silent-refresh logic off 401, so a 403 makes them log the user out instead of refreshing.

code

http · 9 lines
http
GET /api/orders/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...expired

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="token expired"
Content-Type: application/json

{"code":"TOKEN_EXPIRED","message":"Access token expired"}

go deeper

for a junior

Recall the pair crisply: 401 = who are you / credentials missing or invalid; 403 = known and refused. Mention that 401 asks the client to authenticate.

for a middle

Add the mandatory WWW-Authenticate challenge, the expired-token-is-401 rule, and the concrete effect on client refresh interceptors.

for a senior

Discuss existence-hiding with 404, consistent policy per resource family, and separating 401 vs 403 in logs and dashboards to diagnose key rotation versus role misconfiguration.

for a principal

Frame it as a platform contract: one error taxonomy across services so gateways, SDKs and clients agree on which failures are retryable, plus a deliberate information-disclosure policy for object-level authorization.

## Two different questions HTTP splits "you cannot have this" into two answers. **Authentication** asks *who are you?*; **authorization** asks *are you allowed?* Status **401** is a negative answer to the first, **403** to the second. The names mislead. 401 is spelled "Unauthorized" but means *unauthenticated*. 403 is "Forbidden" and is the real authorization failure. Reading them as "401 = no identity, 403 = identity insufficient" removes almost all confusion. ## 401 and the mandatory challenge RFC 9110 says a 401 response **MUST** include a `WWW-Authenticate` header field containing at least one challenge applicable to the target resource. A challenge names an authentication scheme and optional parameters: - `WWW-Authenticate: Basic realm="admin"` — browsers respond by popping the native credential dialog. - `WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="expired"` — the OAuth 2.0 bearer form (RFC 6750), which lets a client distinguish *expired* from *malformed* and decide whether to refresh. A 401 without any challenge is technically non-conformant, and it is also less useful: the client has no machine-readable hint about how to authenticate. Many APIs ship this bug because a framework filter writes a bare 401. Typical 401 triggers: no `Authorization` header at all; expired or revoked access token; bad signature; wrong audience/issuer on a JWT; session cookie unknown to the store. ## 403 and what it means operationally 403 says: request understood, authorization refused, **do not retry as-is**. The typical causes are role/scope failures (a valid token without the `orders:write` scope), object-level checks (a user asking for a record belonging to another tenant), and non-identity policy (geo-block, IP allowlist, suspended account, read-only maintenance mode). RFC 9110 also allows a server to return 403 rather than reveal that it *could* have succeeded with different credentials. An anonymous request to a never-public resource is a judgement call. If the endpoint accepts credentials, 401 with a challenge is friendlier: it tells the client what to do. If credentials could never help — say, the resource is disabled — 403 is honest. ## The refresh-flow trap Almost every SPA and mobile client implements the same interceptor: *on 401, try the refresh token once, replay the request; on 403, surface a "not allowed" error*. If the API returns 403 for an expired token, users get spurious permission errors and forced logouts. Conversely, if the API returns 401 for a genuine permission failure, clients enter refresh loops — refresh succeeds, request fails 401 again, repeat. Getting this pair right is a contract with your own clients, not pedantry. ## When to hide existence instead A 403 confirms the resource exists. For sensitive object IDs (another tenant's invoice, a private repository) that is an information leak — an attacker can enumerate IDs and read existence from the status code. The common hardening is to return **404** for objects the caller may not see, and reserve 403 for cases where existence is already known to the caller. Pick one policy per resource family and apply it consistently; mixing 403 and 404 for the same class of object re-creates the oracle you were trying to close. ## Body and observability Both statuses may carry a body. Use a machine-readable error shape (a stable `code` plus a human `message`); never echo the token or the internal policy rule that failed. On the server side, log which check failed and the subject identifier — the single most common production complaint is "we return 403 and nobody can tell why". Distinguish, in logs and metrics, authentication failures from authorization failures: a spike in 401s usually means a clock skew, key rotation, or expired secret; a spike in 403s usually means a bad role migration or a misconfigured policy.

  • A client sends a valid but expired access token. Which status do you return and why?
    401, with `WWW-Authenticate: Bearer error="invalid_token"`. Expiry is an authentication failure: the credential is no longer valid, and retrying with a fresh token will succeed. Clients key their silent-refresh interceptors off 401, so returning 403 here causes spurious logouts.
  • An authenticated user requests another tenant's record. Is 403 or 404 the better response?
    Both are defensible. 403 is literally accurate but confirms the record exists, letting an attacker enumerate IDs. For sensitive resources, return 404 so that "absent" and "not yours" are indistinguishable. Whatever you choose, apply it uniformly across the resource family, otherwise the inconsistency itself leaks existence.
  • Is it legal to return 401 without a WWW-Authenticate header?
    No — RFC 9110 makes the header mandatory on 401 responses, and it must contain at least one challenge. Many frameworks emit a bare 401 anyway, so clients still work, but you lose the machine-readable signal that tells a client which scheme to use and why the credential was rejected.

saying these in an interview costs you the question

  • Saying 401 means "you are logged in but not allowed" — that is 403
  • Returning 403 for expired or missing tokens, breaking client refresh flows
  • Omitting WWW-Authenticate on 401 responses
  • Believing 403 must never be used for anonymous requests
  • Returning 200 with an error body instead of 401/403 "because the client handles it"

context

open as a page

The HTTP status codes 400 Bad Request and 422 Unprocessable Content are both client errors. What does each one mean per the HTTP specifications, and how do generic caches, proxies and clients treat them on the wire?

level: middleimportance: should knowfreq 55%

basics

~20 s

400: the server could not parse or accept the message itself — malformed syntax, framing or unparseable content. 422: the media type was understood and the syntax parsed, but the server cannot process the instructions. Both are client errors; neither is auto-retryable or cacheable by default.

open as a page

What is the difference between HTTP status 404 Not Found and 410 Gone, and when is each the right choice?

level: middleimportance: should knowfreq 45%

basics

~20 s

404 says the server found no current representation and gives no promise about the past or future — the resource may appear later. 410 says the resource existed, is intentionally and permanently gone, and clients and crawlers should stop asking and remove links.

open as a page

When must an HTTP server respond with 405 Method Not Allowed, what header is mandatory on that response, and how does it differ from 501 Not Implemented?

level: middleimportance: should knowfreq 40%

basics

~20 s

405 means the target resource exists but does not support the method used. RFC 9110 requires the response to include an Allow header listing the methods it does support, e.g. Allow: GET, HEAD, PUT. 501 means the server does not recognise or implement the method at all.

open as a page

What does HTTP status 409 Conflict mean, and give concrete situations where it is the right answer rather than 400 or 422?

level: seniorimportance: should knowfreq 45%

basics

~20 s

409 means the request is well-formed and valid but conflicts with the current state of the target resource — a duplicate unique key, a lost-update on concurrent edits, or an illegal state transition. The body should explain the conflict so the client can resolve and resubmit.

open as a page

How should an HTTP service signal rate limiting with status 429 Too Many Requests, and what are the two legal formats of the Retry-After header?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Return 429 when a client exceeded its request quota, and include Retry-After telling it when to come back. Retry-After takes either delta-seconds (Retry-After: 120) or an HTTP-date (Retry-After: Wed, 21 Oct 2026 07:28:00 GMT). Clients should back off, not retry immediately.

open as a page