HTTP 401 Unauthorized and WWW-Authenticate
Diagnose HTTP 401 Unauthorized by reading the WWW-Authenticate header, checking token format, and distinguishing expired tokens from missing credentials.
Symptoms
- HTTP 401 with WWW-Authenticate: Bearer realm=...
- Requests work after logging in again but fail after a timeout
- The client sends a token yet still receives 401
Possible causes
- No Authorization header is sent at all
- The token is expired, malformed, or signed with the wrong key
- The scheme is wrong (Basic vs Bearer) or includes stray whitespace
- A clock skew makes a freshly issued token appear expired
Diagnosis steps
Read the WWW-Authenticate challenge
Interpretation
It names the expected scheme and often an error code such as invalid_token, telling you why authentication failed.
Decode the token payload
jwt decode <token> (or base64 the middle segment)Interpretation
Check exp, iss, and aud against the server expectations; a wrong audience or issuer causes rejection.
Confirm the header format
Interpretation
Authorization: Bearer <token> with exactly one space; a missing scheme or extra whitespace is rejected.
Root cause
A 401 means the request lacks valid authentication credentials. The WWW-Authenticate header is the server explaining how to authenticate and, with an error parameter, why the attempt failed. A token can be present but invalid because it is expired, signed by another key, or destined for a different audience.
Fix
Send a correctly formed Authorization header with the right scheme, refresh tokens before expiry, and align issuer, audience, and signing keys between the token issuer and the verifier. Where clock skew is possible, allow small leeway on exp. Never treat 401 as retryable with the same token; obtain a fresh credential first.
Verify the fix
Replay the request with a freshly issued token; it returns 200, and the WWW-Authenticate error parameter no longer appears in the response headers.
Symptoms
The API returns 401 Unauthorized alongside WWW-Authenticate: Bearer. It may appear only after a session ages, or from a particular client that handles headers differently.
Diagnosis
Read the challenge and decode the token:
curl -i https://api.example.com/me
# WWW-Authenticate: Bearer realm="api", error="invalid_token"
# decode the payload (middle segment) to inspect claims
echo '<payload>' | base64 -d
Check exp, iss, and aud against the verifier configuration.
Fix
Send the right scheme and refresh on time:
curl -H 'Authorization: Bearer eyJ...' https://api.example.com/me
Align signing keys and allow small clock leeway.
Verification
Retry with a fresh token; the endpoint returns 200 and the response no longer includes an error in WWW-Authenticate, confirming the credential is now valid.
401 Versus 403
These two codes are often confused, and telling them apart dictates the fix. A 401 means the request lacks valid credentials: send a token, refresh an expired one, or correct its signature. A 403 means the credentials are valid but the identity is not permitted. Clients should retry a 401 only after acquiring new credentials, never by repeating the same request, and they should treat a 403 as a permission problem rather than an authentication one. A server that returns 401 for a permission failure misleads well-behaved clients into pointless token refreshes.