HTTP

Status Code

HTTP 401 Unauthorized: Authentication Required

Learn what 401 Unauthorized means, why it happens, and how to fix authentication errors. Complete guide with examples for APIs and web applications.

5 min read beginner Try in Playground

TL;DR: 401 Unauthorized means authentication required. Provide valid credentials in the Authorization header.

TL;DR: 401 means “who are you?” The server needs you to prove your identity before accessing this resource.

What is 401 Unauthorized?

A 401 error means your request is missing valid authentication credentials. The server doesn’t know who you are and won’t let you in until you prove your identity.

Think of it like a bouncer at a club asking for ID. You might be allowed in, but first you need to show credentials.

GET /api/account HTTP/1.1
Host: api.example.com
```text

Response:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Content-Type: application/json

{
  "error": "unauthorized",
  "message": "Authentication required"
}

The WWW-Authenticate header tells you what type of credentials the server expects.

When 401 Happens

Missing Credentials

GET /api/user/profile HTTP/1.1
Host: api.example.com
# No Authorization header → 401
```text

### Invalid Credentials

```http
GET /api/user/profile HTTP/1.1
Authorization: Bearer invalid_or_expired_token
# Bad token → 401

Expired Token

GET /api/user/profile HTTP/1.1
Authorization: Bearer eyJ...expired...
# Token expired → 401
```text

### Wrong Authentication Scheme

```http
GET /api/user/profile HTTP/1.1
Authorization: Basic dXNlcjpwYXNz
# Server expects Bearer, got Basic → 401

Authentication Methods

Bearer Token (Most Common for APIs)

GET /api/data HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```text

401 Response:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token"

Basic Authentication

GET /admin HTTP/1.1
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
```text

The value is base64-encoded `username:password`.

401 Response:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Admin Area"

API Key

GET /api/data HTTP/1.1
X-API-Key: your-api-key-here
```text

Or in query string (less secure):

```http
GET /api/data?api_key=your-api-key-here
```text

## 401 vs 403: The Key Difference

| Aspect                | 401 Unauthorized          | 403 Forbidden        |
| --------------------- | ------------------------- | -------------------- |
| **Meaning**           | "Who are you?"            | "You can't do that"  |
| **Problem**           | Authentication            | Authorization        |
| **Solution**          | Provide valid credentials | Get more permissions |
| **Retry with login?** | Yes, might work           | No, won't help       |
| **WWW-Authenticate**  | Required                  | Not used             |

### Decision Flow

```text
Request arrives

Has valid credentials?
    No → 401 Unauthorized
    Yes ↓
Has permission for this resource?
    No → 403 Forbidden
    Yes → 200 OK
```text

## Example Responses

### API with Bearer Token

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Content-Type: application/json

{
  "error": "unauthorized",
  "message": "Invalid or expired token",
  "code": "TOKEN_INVALID"
}

Web Application

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Members Only"
Content-Type: text/html

<!DOCTYPE html>
<html>
<body>
  <h1>Authentication Required</h1>
  <p>Please <a href="/login">log in</a> to continue.</p>
</body>
</html>
```text

### OAuth 2.0 Error

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="Token expired"

How to Fix 401 Errors

For Users

  1. Log in again - Your session may have expired
  2. Check credentials - Verify username/password
  3. Clear cookies - Corrupted session data
  4. Check URL - You might be on wrong environment

For Developers

1. Verify Token is Sent

// ❌ Missing Authorization header
fetch('/api/data')

// ✅ Include the token
fetch('/api/data', {
  headers: {
    Authorization: `Bearer ${token}`
  }
})
```javascript

#### 2. Check Token Expiration

```javascript
function isTokenExpired(token) {
  const payload = JSON.parse(atob(token.split('.')[1]))
  return payload.exp * 1000 < Date.now()
}

if (isTokenExpired(token)) {
  token = await refreshToken()
}

3. Handle 401 in API Calls

async function fetchWithAuth(url) {
  let response = await fetch(url, {
    headers: { Authorization: `Bearer ${getToken()}` }
  })

  if (response.status === 401) {
    // Try to refresh token
    const newToken = await refreshToken()
    if (newToken) {
      response = await fetch(url, {
        headers: { Authorization: `Bearer ${newToken}` }
      })
    } else {
      // Redirect to login
      window.location.href = '/login'
    }
  }

  return response
}
```javascript

#### 4. Implement Token Refresh

```javascript
async function refreshToken() {
  const response = await fetch('/api/auth/refresh', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      refreshToken: getRefreshToken()
    })
  })

  if (response.ok) {
    const { accessToken } = await response.json()
    saveToken(accessToken)
    return accessToken
  }

  return null
}

API Design Best Practices

Always Include WWW-Authenticate

// Express.js
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    res
      .status(401)
      .set('WWW-Authenticate', 'Bearer realm="api"')
      .json({ error: 'Invalid or missing token' })
  }
})
```text

### Return Helpful Error Messages

```json
{
  "error": "unauthorized",
  "code": "TOKEN_EXPIRED",
  "message": "Your session has expired",
  "action": "Please log in again"
}

Don’t Leak Information

// ❌ Bad: Reveals user exists
{ "error": "Wrong password for user john@example.com" }

// ✅ Good: Generic message
{ "error": "Invalid credentials" }
```text

## Common Mistakes

### Confusing 401 and 403

```javascript
// ❌ Wrong: Using 401 for authorization
if (!user.isAdmin) {
  return res.status(401).json({ error: 'Not authorized' })
}

// ✅ Correct: 403 for authorization failures
if (!user.isAdmin) {
  return res.status(403).json({ error: 'Admin access required' })
}

Missing WWW-Authenticate Header

// ❌ Wrong: No WWW-Authenticate
res.status(401).json({ error: 'Unauthorized' })

// ✅ Correct: Include the header
res.status(401).set('WWW-Authenticate', 'Bearer').json({ error: 'Unauthorized' })
```javascript

### Not Handling Token Refresh

```javascript
// ❌ Wrong: Just fail on 401
if (response.status === 401) {
  throw new Error('Unauthorized')
}

// ✅ Better: Try to refresh first
if (response.status === 401) {
  const refreshed = await tryRefreshToken()
  if (refreshed) return retry(request)
  redirectToLogin()
}

Security Considerations

Token Storage

// ❌ Avoid: localStorage (XSS vulnerable)
localStorage.setItem('token', token)

// ✅ Better: HttpOnly cookie (for web apps)
// Set by server with Set-Cookie: token=...; HttpOnly; Secure

// ✅ OK: Memory only (for SPAs, lost on refresh)
let token = null
```text

### HTTPS Required

Never send credentials over HTTP:

```text
http://api.example.com/login
https://api.example.com/login
```text

### Rate Limiting

Protect against brute force:

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 60

Try It Yourself

Test authentication in our request builder:

  1. Make a request without Authorization header
  2. Observe the 401 response
  3. Add Authorization: Bearer test-token header
  4. See how the response changes

In Practice

Express.js
// Auth middleware — return 401 if no valid token
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '')
  if (!token) {
    return res.status(401)
      .set('WWW-Authenticate', 'Bearer realm="api"')
      .json({ error: 'Authentication required' })
  }
  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET)
    next()
  } catch {
    res.status(401)
      .set('WWW-Authenticate', 'Bearer error="invalid_token"')
      .json({ error: 'Invalid or expired token' })
  }
}

app.get('/api/profile', requireAuth, (req, res) => res.json(req.user))
Next.js App Router
// app/api/profile/route.ts
import { verifyToken } from '@/lib/auth'

export async function GET(request: Request) {
  const token = request.headers.get('authorization')?.replace('Bearer ', '')
  if (!token) {
    return Response.json(
      { error: 'Authentication required' },
      {
        status: 401,
        headers: { 'WWW-Authenticate': 'Bearer realm="api"' }
      }
    )
  }
  const user = await verifyToken(token)
  if (!user) {
    return Response.json(
      { error: 'Invalid or expired token' },
      { status: 401 }
    )
  }
  return Response.json(user)
}
Django
from django.http import JsonResponse
from functools import wraps

def require_auth(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        token = request.META.get('HTTP_AUTHORIZATION', '').replace('Bearer ', '')
        if not token:
            response = JsonResponse({'error': 'Authentication required'}, status=401)
            response['WWW-Authenticate'] = 'Bearer realm="api"'
            return response
        user = verify_token(token)
        if not user:
            return JsonResponse({'error': 'Invalid token'}, status=401)
        request.user = user
        return view_func(request, *args, **kwargs)
    return wrapper

@require_auth
def profile_view(request):
    return JsonResponse(request.user.to_dict())

Frequently Asked Questions

What does 401 Unauthorized mean?

A 401 error means the request lacks valid authentication credentials. The server cannot verify who you are. You need to provide valid credentials (like a username/password or API token) to access the resource.

What is the difference between 401 and 403?

401 means "I don't know who you are" (authentication failed). 403 means "I know who you are, but you're not allowed" (authorization failed). 401 asks you to log in; 403 says logging in won't help.

How do I fix a 401 error?

Check that you're sending the correct credentials in the Authorization header. Verify your token hasn't expired. Ensure you're using the right authentication method (Bearer, Basic, API key). Check for typos in credentials.

Should 401 responses include WWW-Authenticate header?

Yes, RFC 9110 requires 401 responses to include a WWW-Authenticate header that indicates the authentication scheme the server accepts. This helps clients know how to authenticate.

Can browsers handle 401 automatically?

Yes, for Basic authentication, browsers show a login dialog when receiving 401 with WWW-Authenticate: Basic. For Bearer tokens and API keys, your application must handle the 401 and prompt for credentials.

Keep Learning