- Home
- HTTP Headers
- Authorization Header: HTTP Authentication
Header
Authorization Header: HTTP Authentication
Learn how the Authorization header works, different authentication schemes (Bearer, Basic, API keys), and security best practices.
TL;DR: The Authorization header sends credentials to authenticate requests. Format:
Authorization: <scheme> <credentials>
Syntax
Authorization: <scheme> <credentials>
```text
Common schemes:
- `Bearer` - Token-based (JWT, OAuth)
- `Basic` - Username:password (base64)
- `Digest` - Challenge-response
- `API-Key` - Custom API keys
## Bearer Authentication
Most common for APIs. Sends a token (usually JWT):
```http
GET /api/user HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JavaScript
const response = await fetch('/api/user', {
headers: {
Authorization: `Bearer ${token}`
}
})
```javascript
### Server Validation
```javascript
app.use((req, res, next) => {
const auth = req.headers.authorization
if (!auth?.startsWith('Bearer ')) {
return res.status(401).set('WWW-Authenticate', 'Bearer').json({ error: 'Token required' })
}
const token = auth.slice(7)
try {
req.user = jwt.verify(token, SECRET)
next()
} catch {
res.status(401).json({ error: 'Invalid token' })
}
})
Basic Authentication
Sends base64-encoded username:password:
GET /admin HTTP/1.1
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
```javascript
The value decodes to `username:password`.
### JavaScript
```javascript
const credentials = btoa(`${username}:${password}`)
const response = await fetch('/admin', {
headers: {
Authorization: `Basic ${credentials}`
}
})
Server Validation
function basicAuth(req, res, next) {
const auth = req.headers.authorization
if (!auth?.startsWith('Basic ')) {
return res.status(401).set('WWW-Authenticate', 'Basic realm="Admin"').send()
}
const [username, password] = Buffer.from(auth.slice(6), 'base64').toString().split(':')
if (username === 'admin' && password === 'secret') {
next()
} else {
res.status(401).json({ error: 'Invalid credentials' })
}
}
```text
## API Key Authentication
Custom scheme or header:
```http
# In Authorization header
Authorization: Api-Key your-api-key-here
# Or custom header (common alternative)
X-API-Key: your-api-key-here
// Validation
const apiKey = req.headers['x-api-key'] || req.headers.authorization?.replace('Api-Key ', '')
if (!apiKey || !isValidApiKey(apiKey)) {
return res.status(401).json({ error: 'Invalid API key' })
}
```javascript
## Bearer vs Basic vs API Key
| Aspect | Bearer | Basic | API Key |
| --------- | -------------------- | ------------------- | ---------------- |
| Security | High (tokens expire) | Low (password sent) | Medium |
| Stateless | Yes | Yes | Yes |
| Revocable | Yes (token expiry) | Change password | Yes |
| Use case | APIs, OAuth | Simple auth | Third-party APIs |
## Security Best Practices
1. **Always use HTTPS** - Credentials are visible in plain text
2. **Short token expiry** - Limit damage from stolen tokens
3. **Don't log tokens** - Exclude Authorization from logs
4. **Use HttpOnly cookies** - For browser apps, consider cookies instead
5. **Validate on every request** - Don't trust cached auth state
```javascript
// ❌ Bad: Logging sensitive headers
console.log('Request headers:', req.headers)
// ✅ Good: Exclude sensitive data
const { authorization, ...safeHeaders } = req.headers
console.log('Request headers:', safeHeaders)
Related
- 401 Unauthorized - Authentication required
- 403 Forbidden - Authenticated but not authorized
- WWW-Authenticate header - Server’s auth requirements
- Authentication guide - Complete auth patterns
Token Storage and the Authorization Header
One of the most debated questions in web security is where to store authentication tokens: in localStorage (and send via Authorization header) or in HttpOnly cookies (sent automatically by the browser). Both approaches have legitimate use cases and different threat models.
Storing tokens in localStorage and sending them via the Authorization: Bearer header protects against CSRF attacks because JavaScript must explicitly attach the token to each request. A malicious site cannot trigger authenticated requests without access to the token. However, this approach is vulnerable to XSS: if an attacker can run JavaScript on your page, they can read localStorage and steal the token.
Storing tokens in HttpOnly cookies protects against XSS token theft because JavaScript cannot read HttpOnly cookies. However, cookies are sent automatically with every request to the matching domain, which requires CSRF protection (via SameSite=Strict or CSRF tokens). The SameSite=Lax default in modern browsers provides reasonable CSRF protection for most use cases.
For single-page applications that call APIs on the same domain, HttpOnly cookies with SameSite=Lax and Secure is generally the more secure choice. For mobile apps or SPAs calling third-party APIs where cookies are impractical, Authorization: Bearer with short-lived tokens and refresh token rotation is the standard approach. The key principle is that token lifetime should be as short as practical: short-lived access tokens (15 minutes) combined with longer-lived refresh tokens (days) limit the damage from any single token compromise.
In Practice
Express.js
// Middleware to verify Bearer token
function requireAuth(req, res, next) {
const auth = req.headers.authorization
if (!auth?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing token' })
}
try {
req.user = jwt.verify(auth.slice(7), process.env.JWT_SECRET)
next()
} catch {
res.status(401).json({ error: 'Invalid token' })
}
}
app.get('/api/profile', requireAuth, (req, res) => {
res.json(req.user)
})
// Client: attach token to requests
const res = await fetch('/api/profile', {
headers: { Authorization: `Bearer ${token}` }
})Next.js App Router
// middleware.ts — protect 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 ', '')
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
# Using Django REST Framework
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
class BearerAuthentication(BaseAuthentication):
def authenticate(self, request):
auth = request.META.get('HTTP_AUTHORIZATION', '')
if not auth.startswith('Bearer '):
return None
token = auth[7:]
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id=payload['user_id'])
return (user, token)
except Exception:
raise AuthenticationFailed('Invalid token')Frequently Asked Questions
What is the Authorization header?
The Authorization header sends credentials to authenticate a request. It contains the authentication scheme (like Bearer or Basic) followed by the credentials.
What is the difference between Bearer and Basic authentication?
Basic sends base64-encoded username:password. Bearer sends a token (like JWT). Bearer is more secure and flexible for APIs.
Should I use Authorization header or cookies?
Use Authorization header for APIs and stateless auth. Use cookies for browser-based sessions with CSRF protection. APIs typically prefer Bearer tokens.
Is the Authorization header secure?
Only over HTTPS. The header is sent in plain text, so always use TLS. Never send credentials over HTTP.