Unauthorized
ActiveHTTP 401 Unauthorized indicates the request lacks valid authentication credentials for the target resource. Defined in RFC 9110 §15.5.2. The response must include a WWW-Authenticate header indicating the authentication scheme.
Description
The 401 Unauthorized status code indicates that the request lacks valid authentication credentials for the target resource. The server MUST send a WWW-Authenticate header field containing at least one challenge – without it, the client has no way to know how to authenticate.
Despite its name, 401 is about authentication (proving who you are), not authorization (what you are allowed to do). If the client is authenticated but lacks permission, use 403 Forbidden instead.
Browsers receiving 401 with WWW-Authenticate: Basic will show a native login dialog. API clients receiving 401 with WWW-Authenticate: Bearer should refresh their access token and retry.
Examples
GET /api/account HTTP/1.1
Host: api.example.comHTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Content-Type: application/json
{"error": "unauthorized", "message": "Valid authentication token required"}Edge Cases
- •RFC 9110 requires WWW-Authenticate in every 401 response. Many frameworks omit it for JSON APIs – add it explicitly with the expected scheme (Bearer, Basic, Digest).
- •Expired JWT: check the exp claim in the token. The client must refresh and retry. Log the expiry reason, not just '401 Unauthorized'.
- •Wrong token scope: if the token is valid but lacks permission for this endpoint, return 403 not 401. 401 means authentication failed; 403 means authenticated but forbidden.
- •Clock skew: JWT nbf and iat validation fails when server and client clocks differ by more than the tolerance (typically 60 seconds). Check NTP sync on servers.
- •CORS preflight: an OPTIONS request with no credentials should return 200/204. Returning 401 on OPTIONS breaks CORS for all cross-origin requests to that endpoint.
- •Cookie vs Authorization header confusion: the client is sending credentials in the wrong place. Check whether your framework reads from the header or cookie, not both.
When You'll See This
- →Missing or expired authentication token
- →Invalid API key
- →Session expired
- →Basic auth credentials rejected
Implementation References
| Language | Constant |
|---|---|
| Go | http.StatusUnauthorized |
| Rust | http::StatusCode::UNAUTHORIZED |
| Python | http.HTTPStatus.UNAUTHORIZED |
| Node.js | http.STATUS_CODES[401] |
| .NET | HttpStatusCode.Unauthorized |
| Java | HttpURLConnection.HTTP_UNAUTHORIZED |
History
Introduced in HTTP/1.0 (RFC 1945, 1996). Originally designed for HTTP Basic/Digest authentication. Now widely used with Bearer tokens (OAuth 2.0).
Related Status Codes
Related Headers
FAQ
What is the difference between 401 and 403?
401 means you're not authenticated (not logged in or token expired). 403 means you're authenticated but don't have permission.
Why is it called 'Unauthorized' if it means unauthenticated?
It's a historical naming mistake. 401 actually means 'unauthenticated'. 403 Forbidden is about authorization.