HTTP 401 Unauthorized
The request has no valid authentication credentials — you need to log in or send a valid token.
Meaning
Despite the name, 401 is about authentication (who you are), not authorization. The server is saying “I don’t know who you are” — the credentials are missing, expired, malformed or wrong.
A correct 401 response includes a WWW-Authenticate header telling the client which scheme to use (e.g. Bearer). If you are logged in but still not allowed, the right code is 403.
Common causes
- No
Authorizationheader sent - Expired access token / JWT
- Wrong scheme, e.g.
Authorization: <token>instead ofAuthorization: Bearer <token> - Invalid or revoked API key
- Session cookie not sent (cross-site request without
credentials: 'include', SameSite rules) - Proxy or web server strips the Authorization header before it reaches the app (common with Apache + PHP-FPM)
- Clock skew making a valid JWT look expired or not-yet-valid
⚡ Quick fix
- Confirm the request actually carries the
Authorizationheader (browser DevTools → Network) - Use the exact scheme the API expects:
Bearer <token> - Refresh or regenerate the token / API key
- For cookies across origins, send
credentials: 'include'and setSameSite=None; Secure - On Apache, pass the Authorization header through to PHP (see fix below)
Detailed fix by platform
JavaScript
- Send the token with the right scheme and handle refresh on 401.javascript
const res = await fetch('/api/me', { headers: { Authorization: `Bearer ${accessToken}` }, credentials: 'include', // only if you rely on cookies }); if (res.status === 401) { await refreshToken(); // then retry once }
Apache
- Apache with CGI/FPM often drops
Authorization. Add to.htaccess: - Forward the header to PHP:apache
RewriteEngine On RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
PHP
- Read the header from
$_SERVER['HTTP_AUTHORIZATION']and fall back toREDIRECT_HTTP_AUTHORIZATION. - Return 401 with
WWW-Authenticate: Bearerfor missing/invalid tokens and 403 for valid-but-forbidden users.
Nginx
- If Nginx proxies to an app, make sure you don’t override the header: avoid
proxy_set_header Authorization "";. - For basic auth zones, check
auth_basic_user_filepath and password hash format.
Code examples
Decode a JWT to check expiry
bash
# Payload is the middle part; "exp" is a Unix timestamp
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null; echo
date +%s # compare with nowIf exp is in the past (or your server clock is wrong), the server will answer 401.
How to diagnose
- Request — Is an Authorization header or session cookie present in the outgoing request?
- Format — Is the scheme right (Bearer/Basic) and the token complete?
- Token validity — Expired? Revoked? Issued for a different audience/environment?
- Transport — Does a proxy, CDN or Apache config strip the header?
- Server — What does the auth middleware log as the rejection reason?
🧠 Still stuck? Analyze your error
Paste the full message, response headers or stack trace — we'll detect the platform and point to the most likely cause.
Was this page helpful?
Report a correction or suggest an improvement
Last updated 2 Oct 2026