How to Fix "401 Unauthorized" with Bearer Token Header in API
Quick answer
Receiving an HTTP 401 Unauthorized status code when passing an Authorization: Bearer <token> header is a common hurdle in API integration. This response...
Receiving an HTTP 401 Unauthorized status code when passing an Authorization: Bearer <token> header is a common hurdle in API integration. This response indicates that the target server reached the authentication middleware, but rejected the credentials due to formatting mistakes, signature validation failures, token expiration, or stripped authorization headers. Resolving this issue involves verifying header syntax, auditing middleware token parsers, and checking API proxy configurations.
The Problem
When sending requests or configuring components in your development environment, the application or HTTP client fails with the following exact error trace:
# Example 1: Standard HTTP Response Header Failure
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="The access token expired"
Content-Type: application/json
{"statusCode": 401, "message": "Unauthorized"}
# Example 2: Express / Passport.js Authentication Failure
UnauthorizedError: No authorization token was found
at jwtMiddleware (/app/middleware/auth.js:18:13)
# Example 3: Python FastAPI / Starling HTTP Exception
INFO: 127.0.0.1:51234 - "GET /api/v1/protected HTTP/1.1" 401 Unauthorized
detail: "Not authenticated"
This error halts script execution, prevents API communication, or results in immediate client-side connection drops.
Why It Happens
This failure occurs due to misconfigurations at the networking, cryptographic, or application protocol layer. The primary causes include:
- Malformed Authorization Header Syntax: The header value lacks the mandatory space between
Bearerand the token string (e.g.,Bearer<token>) or uses lowercasebearerwhen backend frameworks strictly mandate capital case. - Expired Access Token (exp Claim): The JWT access token lifetime has elapsed and the API middleware strictly rejects requests where
exp < currentTime. - Apache/Nginx Stripping Authorization Headers: Apache HTTP Server (via
mod_cgior FastCGI) or WSGI application servers drop theAuthorizationheader by default unless explicit rewrite rules are enabled. - Audience or Issuer Mismatch: The token signature is valid, but token claims (
audoriss) do not match the expected API resource identifier configured in backend security settings.
The Fix
Follow these step-by-step solutions to resolve the error in your environment.
Step 1: Validate Header Formatting in Client Requests
Ensure the HTTP Authorization header adheres strictly to standard Bearer token syntax. Execute a test request using cURL:
curl -i -X GET https://api.example.com/v1/user -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" -H "Accept: application/json"
Step 2: Configure Apache / WSGI to Pass Authorization Headers
If hosting PHP, Python (Django/Flask), or Ruby applications behind Apache, add the following directive to your .htaccess or virtual host configuration to prevent Apache from stripping the header:
# Enable Authorization header forwarding in Apache
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
# Alternative mod_rewrite directive
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
Step 3: Implement Token Expiration and Header Parsing Middleware Guard
In your backend API authentication middleware, parse the token robustly and handle bearer prefix variations and explicit logging:
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
// Support both case formats and extract token safely
const token = authHeader && authHeader.startsWith('Bearer ')
? authHeader.split(' ')[1]
: null;
if (!token) {
return res.status(401).json({ error: '401 Unauthorized', message: 'Bearer token missing or malformed' });
}
jwt.verify(token, process.env.JWT_PUBLIC_KEY, (err, user) => {
if (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: '401 Unauthorized', message: 'Token has expired' });
}
return res.status(401).json({ error: '401 Unauthorized', message: 'Invalid token signature' });
}
req.user = user;
next();
});
}
Still Not Working?
CORS Preflight OPTIONS Requests Stripping Authorization
Browsers issuing cross-origin requests send an unauthenticated OPTIONS preflight request prior to sending the actual HTTP request. If your API security layer or framework middleware enforces authentication on OPTIONS endpoints, the browser receives a 401 Unauthorized during preflight and never attempts the actual request. Ensure your authentication middleware explicitly skips validation for req.method === 'OPTIONS'.