HTTP Status CodesRunbook 0/4 done
401

HTTP Status Codes Error 401: Unauthorized

Error✕ Not retryable · fix the payloadHTTP 401 UnauthorizedAPI Gateway
TL;DR · 10-second read

The request has no valid credentials: the token is missing, malformed or expired. Send Authorization: Bearer <token>, and refresh the token on 401 before retrying once.

Identify: does your log match?

What you see · exact log / response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="The access token expired"
Content-Type: application/json

{"error":"invalid_token","error_description":"The access token expired"}
  1. High

    Expired access token

    Short-lived JWTs (often 5–60 min) expire while the client keeps using them. The server answers 401 with error="invalid_token".

  2. Medium

    Authorization header stripped

    A redirect to another host, a proxy or a CORS preflight drops the Authorization header, so the server sees an anonymous request.

  3. Medium

    Wrong scheme or format

    Sending the raw token without "Bearer ", using Basic for a Bearer API, or a stray newline in an env var.

Fix

Refresh once on 401, then retry

✕ Broken · TypeScript
const res = await fetch('/api/me', { headers: { Authorization: token } });
✓ Fixed · TypeScript
async function api(path: string, init: RequestInit = {}, retried = false): Promise<Response> {
  const res = await fetch(path, {
    ...init,
    headers: { ...init.headers, Authorization: `Bearer ${getAccessToken()}` },
  });
  if (res.status === 401 && !retried) {
    await refreshAccessToken();
    return api(path, init, true);
  }
  return res;
}

A single refresh-and-retry handles expiry without loops. Prefix the token with the Bearer scheme.

Verify: reproduce it on purpose

Reproduce locally · curl
$ curl -i https://httpbin.org/bearer

Run it against a dev environment to trigger 401 on purpose and test your error handler.

Prevent

  • Always return WWW-Authenticate with a 401.
  • Refresh tokens proactively a minute before exp.
  • Do not follow cross-origin redirects with credentials.

FAQ

What is the difference between 401 and 403?

401 means "who are you?" — authentication failed or is missing. 403 means "I know who you are, and you are not allowed".

Should a wrong password return 401?

For an API call with bad credentials, yes. A login form endpoint may also return 400 or 422 with a generic message.

Why do I get 401 only in production?

Often a reverse proxy strips Authorization or the env var holding the secret differs. Log whether the header is present (not its value).

Related errors

Last verified 2026-10-21 · Source: IANA HTTP Status Code Registry (RFC 9110) · Official docs ↗ · ~40,500 searches/mo