Request header
> Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...Authorization Header: Bearer, Basic and 401 Fixes
Authorization header carries credentials as scheme plus token. Bearer and Basic formats, WWW-Authenticate, why proxies strip it, redirects, CORS and storage.
- Direction
- Request
- Category
- Authentication
- Spec
- RFC 9110 §11.6.2
- JS can set it
- Yes
- CORS-safelisted
- No: may trigger a preflight
On this page
TL;DR:
Authorization: <scheme> <credentials>proves who is calling. UseBearer <token>for APIs andBasic base64(user:pass)only over HTTPS; if the server says 401, check theWWW-Authenticateresponse header for the scheme it wants.
Format
GET /api/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.abc123
A missing or rejected credential gets a challenge that names the scheme:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="The access token expired"
RFC 9110 section 11.6.2 defines the header; schemes are registered with IANA. Common ones: Basic (RFC 7617), Bearer (RFC 6750), Digest (rare), Negotiate (Kerberos/NTLM), AWS4-HMAC-SHA256 (SigV4), and DPoP (RFC 9449, sender-constrained tokens). “API key” is not a scheme; people send keys as Bearer, or in X-API-Key.
Bearer
Whoever holds the token can use it, which is why lifetime and transport matter:
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me
The 401 WWW-Authenticate error values come from RFC 6750: invalid_request (400), invalid_token (401), insufficient_scope (403). Expired tokens, wrong audience, clock skew on JWT exp or nbf, and a missing Bearer prefix are the usual reasons for invalid_token.
Basic
base64("user:pass"), sent on every request:
curl -u alice:s3cret https://example.com/admin -v
# > Authorization: Basic YWxpY2U6czNjcmV0
echo YWxpY2U6czNjcmV0 | base64 -d
base64 is encoding, not encryption. Browsers show a native login prompt when a 401 carries WWW-Authenticate: Basic realm="Admin". Basic is fine for internal tools behind TLS, bad for anything else. Compare passwords with a constant-time function and rate-limit attempts.
Why the header goes missing
- Cross-origin redirect. Per the Fetch Standard, browsers strip
Authorizationwhen a redirect leaves the origin; curl does the same unless you pass--location-trusted. A 301 fromhttp://tohttps://or fromexample.comtowww.example.comdrops it. - Proxy or FastCGI. Apache with
mod_fcgid/PHP-FPM may not pass it; addSetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1orRewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]. nginx forwards it by default withproxy_pass, butproxy_set_header Authorization ""or a customfastcgi_param HTTP_AUTHORIZATION $http_authorization;is needed for FastCGI. AWS API Gateway authorizers and some CDNs may replace it. - CORS.
Authorizationis not a safelisted header, so a cross-origin call triggers a preflight, and the response needsAccess-Control-Allow-Headers: authorization. A wildcard*in that header does not coverAuthorization; list it explicitly. See Access-Control-Allow-Headers. - Caching. A shared cache will not store a response to a request with
Authorizationunless the response haspublic,s-maxageormust-revalidate(RFC 9111 section 3.5). AddVary: Authorizationif you intentionally cache.
401 or 403
401 means the server did not accept who you are, so send credentials or fresh ones. 403 means it knows who you are and refuses. See 401 vs 403, 401 and 403.
Token storage in browsers
A token in localStorage sent as Bearer is immune to CSRF, but any XSS can read and exfiltrate it. An HttpOnly; Secure; SameSite=Lax cookie cannot be read by script but is attached automatically, so you need CSRF defences for state-changing requests. For a first-party SPA the cookie (or a backend-for-frontend that holds tokens server-side) is usually the safer choice. For mobile apps and third-party API clients, use Authorization: Bearer with short-lived access tokens (minutes) and rotating refresh tokens. See Cookie security and Authentication.
Security checklist
- Only over HTTPS; add Strict-Transport-Security.
- Redact
AuthorizationandCookiein access logs, APM, and error reporters. - Never put credentials in the URL; they leak via
Referer, history and logs. - Pin the verification algorithm for JWTs and validate
aud,iss,exp. - Send
Cache-Control: no-storeon token endpoints.
Related
In practice
Express.js
// Middleware to verify a Bearer token
function requireAuth(req, res, next) {
const auth = req.headers.authorization
if (!auth || !/^Bearer /i.test(auth)) {
return res
.status(401)
.set('WWW-Authenticate', 'Bearer realm="api"')
.json({ error: 'Missing token' })
}
try {
// Pin the algorithm: never let the token choose it
req.user = jwt.verify(auth.slice(7), process.env.JWT_SECRET, { algorithms: ['HS256'] })
next()
} catch {
res
.status(401)
.set('WWW-Authenticate', 'Bearer error="invalid_token"')
.json({ error: 'Invalid token' })
}
}
app.get('/api/profile', requireAuth, (req, res) => res.json(req.user))Next.js App Router
// middleware.ts: protect API routes globally
import { NextRequest, NextResponse } from 'next/server'
import { verifyToken } from '@/lib/auth'
export async function middleware(request: NextRequest) {
const token = request.headers.get('authorization')?.replace(/^Bearer /i, '')
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const payload = await verifyToken(token)
if (!payload) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
}
return NextResponse.next()
}
export const config = { matcher: '/api/:path*' }Django
# Django REST Framework
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
class BearerAuthentication(BaseAuthentication):
def authenticate_header(self, request):
return 'Bearer realm="api"' # makes DRF send 401 + WWW-Authenticate
def authenticate(self, request):
auth = request.META.get('HTTP_AUTHORIZATION', '')
if not auth.startswith('Bearer '):
return None
try:
payload = jwt.decode(auth[7:], settings.SECRET_KEY, algorithms=['HS256'])
return (User.objects.get(id=payload['user_id']), auth[7:])
except Exception:
raise AuthenticationFailed('Invalid token')Frequently asked questions
What is the Authorization header?
It is a request header that carries credentials for the target server in the form "Authorization: <scheme> <credentials>", for example Bearer followed by an access token or Basic followed by base64 of user:password. A server that rejects it answers 401 with a WWW-Authenticate challenge.
What is the difference between Bearer and Basic authentication?
Basic sends base64(username:password) on every request, which is trivially decoded, so it needs HTTPS and a dedicated credential. Bearer sends an opaque or signed token that grants access to whoever holds it, so it also needs HTTPS and short lifetimes. Neither is inherently more secure; the token can be scoped and revoked, which a password usually cannot.
Why is my Authorization header missing on the server?
Common causes: a redirect to a different origin (fetch and curl drop it), a proxy or Apache/CGI configuration that does not pass it through, a CORS preflight that did not list authorization in Access-Control-Allow-Headers, or the client sending the token in another header. Check the raw request at the first server that sees it.
Should I use the Authorization header or cookies?
Use Authorization for APIs consumed by non-browser clients and third parties. For a browser app that calls only its own backend, an HttpOnly, Secure, SameSite cookie keeps the token out of reach of XSS, at the cost of needing CSRF protection.
Is the Authorization header secure?
Only over TLS. On plain HTTP it is readable by anyone on the path. Even over HTTPS it can leak through access logs, error reports and proxies, so redact it from logs and never put credentials in URLs.
Is Authorization case sensitive?
The header name is not, and HTTP/2 and HTTP/3 lowercase it. The scheme name is case-insensitive by RFC 9110, but many servers wrongly compare "Bearer " exactly, so send it as Bearer.
Sources
Related
JWT vs Session Cookies: Logout and Security
Compare stateless JWT bearer tokens with opaque session cookies: revocation, rotation, request size, browser storage, CSRF, XSS and algorithm validation.
Authentication-Info Header
Learn how Authentication-Info provides additional authentication data in responses to successful requests. Covers digest authentication and session info.
WWW-Authenticate
Learn how the WWW-Authenticate header specifies authentication methods required to access protected resources. Understand Basic, Bearer, and Digest schemes.
Proxy-Authenticate Header
Learn how the Proxy-Authenticate header challenges clients for credentials when accessing resources through a proxy. Understand proxy authentication schemes.
Proxy-Authorization Header
Learn how Proxy-Authorization provides credentials to access resources through a proxy server. Understand proxy authentication schemes and security.