skip to content

questions

4

Walk through the HTTP challenge-response authentication flow: what does a 401 Unauthorized response carrying a WWW-Authenticate header tell the client, and what exactly does the client send back?

level: juniorimportance: must knowfreq 72%

answer

  1. 401 challenges, client re-sends
  2. WWW-Authenticate -> Authorization
  3. scheme + realm = protection space
  4. stateless: credentials every request
  5. 401 unauthenticated, 403 forbidden

basics

~20 s

The server refuses with 401 and a WWW-Authenticate header naming an authentication scheme, usually plus a realm. The client gets credentials for that scheme and retries the same request with an Authorization header. HTTP is stateless, so the header repeats on every later request.

solid answer

~50 s

HTTP authentication is a generic **challenge-response** framework (RFC 9110 section 11). The server declines with **401 Unauthorized** and must include **WWW-Authenticate** — the challenge. That header names an auth **scheme** (`Basic`, `Digest`, `Bearer`, `Negotiate`, ...) and usually parameters, most often `realm="..."`, which labels the protection space the credentials apply to. The client picks a scheme it supports, obtains credentials (a browser prompt, config, a token endpoint), and **re-sends the identical request** with `Authorization: <scheme> <credentials>`. Nothing server-side is implied: HTTP is stateless, so the header is resent on every request into that protection space. Two practical points. Clients normally authenticate **preemptively** — they send `Authorization` on the first request and never see the 401 — so the challenge mostly matters as the failure signal. And 401 means *missing or bad credentials*; **403 Forbidden** means the credentials were understood but this principal is not allowed, so a 403 carries no challenge and retrying with the same credentials is pointless.

code

http · 14 lines
http
GET /reports/2026 HTTP/1.1
Host: api.example.com

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="reports", error="invalid_token"
Content-Length: 0

GET /reports/2026 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json

go deeper

for a junior

Recall the loop cleanly: 401 plus WWW-Authenticate names a scheme, client retries with Authorization, credentials repeat on every request, and 401 is not 403.

for a middle

Add the mechanics: scheme plus realm grammar, protection space and credential caching, preemptive auth to save a round trip, and why TLS is mandatory.

for a senior

Talk about operating it: diagnosing prompt loops, 401 without a challenge breaking clients, and why responses to Authorization-bearing requests must not be stored by shared caches.

for a principal

Frame the choice of framework itself: when native HTTP auth beats cookie or token-in-body schemes, how challenge semantics interact with gateways and CDNs, and what the 401/403/404 disclosure policy should be platform-wide.

## The framework HTTP has no built-in login page and no session. Instead RFC 9110 section 11 (which absorbed RFC 7235) defines a tiny, scheme-agnostic **authentication framework**: the server *challenges*, the client *responds with credentials*, and the server re-evaluates the request. Everything specific — how credentials are computed, whether they expire, whether they are a password or a token — lives in a pluggable **authentication scheme**. Four header fields carry the whole thing: - **WWW-Authenticate** — sent by an origin server on **401 Unauthorized**; one or more challenges. - **Authorization** — sent by the client; credentials for the origin server. - **Proxy-Authenticate** — sent by a proxy on **407 Proxy Authentication Required**. - **Proxy-Authorization** — sent by the client; credentials for the *next* proxy. ## Anatomy of a challenge A challenge is a scheme token followed by either a single opaque `token68` blob or a comma-separated list of `name=value` auth-params: `WWW-Authenticate: Basic realm="ops-console", charset="UTF-8"` The scheme name is **case-insensitive** (`bearer` equals `Bearer`), and so are parameter names. `realm` is the one parameter defined for all schemes: an opaque string naming a **protection space** so a user agent can cache and reuse the right credentials. The protection space is scoped to the origin (scheme + host + port) plus the realm value — credentials cached for one realm must not be sent to another. Realms are shown to humans in browser prompts, so they should be short and non-sensitive, never a stack trace or a username list. ## The response half The client re-sends the *same* request, now with `Authorization: <scheme> <credentials>`. There is no negotiation state kept on the server; a REST/API client typically hardcodes the scheme it uses. If the credentials are rejected, the server answers 401 again — a browser then re-prompts, which is why a loop of prompts usually means bad credentials rather than a broken flow. Because every request carries credentials, **transport security matters**: `Basic` credentials are base64, which is encoding not encryption, and bearer tokens are equally replayable. Send them over TLS only. ## Preemptive authentication Waiting for a 401 costs a round trip and, for non-idempotent requests, means uploading the body twice (or dancing with `Expect: 100-continue`). Most API clients therefore attach `Authorization` from the start. Browsers do the same after the first challenge: once they have credentials for a realm they send them preemptively to URIs at or below the challenged path. So in production the challenge is often visible only when something is wrong. ## 401 versus 403 This is the classic follow-up. **401** = *unauthenticated*: the request lacked usable credentials, and the response describes how to supply them. **403** = *authenticated but not permitted*, or the server refuses to say more; it carries no `WWW-Authenticate` because there is nothing useful to retry. Returning 403 for a missing token robs the client of the challenge; returning 401 for a permission failure invites clients into a retry loop. Some APIs deliberately return 404 instead of 403 to avoid confirming a resource exists — a defensible privacy choice, but a conscious one. ## Caching interaction A request with an `Authorization` header is user-specific, so a shared cache must not reuse its response for other users unless the response explicitly permits it (`public`, `s-maxage`, or `must-revalidate`). Forgetting this is how one user's data ends up served to another from a CDN. ## What breaks in practice - 401 sent **without** `WWW-Authenticate` — a spec violation; well-behaved clients (curl `--anyauth`, HTTP libraries, browsers) then have no idea which scheme to use and cannot prompt. - A challenge naming a scheme the client does not implement — the client must ignore that challenge and, if none is left, treat the response as an error. - Using 401 to mean "session expired" while the app actually uses cookies: the honest signal is still 401, but there is no HTTP-level challenge for cookie login, so teams send `WWW-Authenticate` with a real scheme or accept that the status alone is the contract.

  • Must a client wait for a 401 before sending Authorization?
    No. Clients may authenticate preemptively by attaching the Authorization header to the very first request, and most API clients do. This saves a round trip and avoids re-sending a request body. The challenge then only appears when credentials are missing, expired or wrong.
  • What is the realm parameter actually for?
    It labels a protection space so a user agent knows which cached credentials belong where. The space is the origin plus the realm string, so credentials cached for realm "admin" must not be auto-sent to realm "public" on the same host. It is displayed to users, so keep it short and free of sensitive detail.
  • When would you return 403 instead of 401?
    When the request carried valid credentials but the authenticated principal is not allowed to do this. A 403 carries no WWW-Authenticate because re-authenticating with the same identity will not help. Use 401 strictly for missing, malformed or rejected credentials.

A doorman turns you away and says which kind of pass he accepts and which building wing it is for; you go get that pass and present it every single time you walk through, because he never remembers you.

saying these in an interview costs you the question

  • Saying 401 means 'forbidden' and 403 means 'not logged in' — the two are swapped
  • Claiming the server keeps an authentication session after the first successful 401 retry; HTTP is stateless and credentials must be resent
  • Returning 401 with no WWW-Authenticate header and calling it correct
  • Thinking Basic's base64 credentials are encrypted, so TLS is optional
  • Believing the client must always trigger a 401 before it may send Authorization

context

open as a page

HTTP defines both 401 Unauthorized with WWW-Authenticate and 407 Proxy Authentication Required with Proxy-Authenticate. What is the difference, and which request header answers each?

level: middleimportance: should knowfreq 40%

basics

~20 s

401 plus WWW-Authenticate comes from the origin server (or a gateway acting as one) and is answered with Authorization, which travels end to end. 407 plus Proxy-Authenticate comes from the intermediary proxy you are talking to and is answered with Proxy-Authorization, which that proxy consumes and removes.

open as a page

An HTTP response returns 401 with more than one WWW-Authenticate challenge, for example both a Negotiate challenge and a Basic one. How should a client handle that, and what makes the challenge list hard to parse correctly?

level: seniorimportance: should knowfreq 26%

basics

~20 s

A server may offer several schemes; the client picks the strongest one it supports and ignores the rest, answering with a single Authorization header. Parsing is tricky because challenges are comma-separated and so are their auth-params, so a naive split on commas cannot tell where one challenge ends.

open as a page

A team wants an in-house HTTP authentication scheme sent as 'Authorization: AcmeSig <signature>' with a matching WWW-Authenticate challenge. What would you weigh before approving that, and what does the IANA HTTP Authentication Scheme Registry have to do with it?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Scheme names are a shared namespace registered with IANA, so an unregistered name risks collision and no generic client will understand it. Prefer a registered scheme, most often Bearer. Invent one only for a real gap such as request signing, then define its grammar, error signalling and registration path.

open as a page