Guides

7 min readUpdated

401 Unauthorized error: what it means and how to fix it

Quick answer

A 401 Unauthorized error means the server refused the request because it carried no valid authentication credentials: no login at all, a wrong password, or an expired or invalid token. The server must say how to authenticate in a WWW-Authenticate header. Visitors usually fix it by signing in again or clearing the site's cookies. Developers should check the token, and whether a proxy, redirect or cache is dropping the Authorization header or replaying an old 401.

01What does a 401 error mean?

RFC 9110 defines 401 as a request that "has not been applied because it lacks valid authentication credentials for the target resource". Despite the name, it is about authentication, who you are, not authorisation, what you may do. If the request did include credentials, the 401 means they were refused.

The same section adds a hard rule: the server generating a 401 MUST send a WWW-Authenticate header with at least one challenge, which names the scheme the client should use, such as Basic or Bearer. A 401 without that header breaks the specification and leaves clients guessing, so it is the first thing to check on an API that returns one.

A sudden wave of 401s on a login form or API from many addresses has a different meaning: it is the typical footprint of credential stuffing, where stolen username and password pairs are tried in bulk.

02401 vs 403 vs 407: which code applies?

The three codes look alike but ask different parties for different things. For OAuth bearer tokens, RFC 6750 maps its error values onto them: invalid_token goes with 401, insufficient_scope with 403, and a malformed request (invalid_request) with 400 Bad Request.

SituationStatusRequired headerWhat the client should do
No credentials sent to a protected resource401 UnauthorizedWWW-AuthenticateSend credentials in the Authorization header
Credentials sent but wrong, expired or revoked401 UnauthorizedWWW-Authenticate, e.g. error="invalid_token"Sign in again or refresh the token
Valid credentials, but not allowed this resource403 ForbiddenNone (Bearer may add error="insufficient_scope")Do not repeat with the same credentials
Refused for reasons unrelated to credentials (IP, rule, region)403 ForbiddenNoneRead the page; contact the site
A forward proxy on your network wants a login407 Proxy Authentication RequiredProxy-AuthenticateSend Proxy-Authorization to that proxy
The server wants to hide that the resource exists404 Not FoundNoneNothing to fix from the client side

03What does the WWW-Authenticate header tell you?

The challenge names the scheme, and the scheme tells you what kind of credential the server expects. A Basic challenge makes browsers show their own sign-in prompt; a Bearer challenge is addressed to software, so a browser just displays the page.

Basic is encoding, not encryption: the header in the trace below, dXNlcjpwYXNzd2Q=, is simply user:passwd in base64. RFC 7617 states plainly that Basic is not a secure method unless the connection is protected by TLS.

SchemeDefined inExample challengeTest with curl
BasicRFC 7617Basic realm="Staging"curl -u user:password URL
Bearer (OAuth 2.0)RFC 6750Bearer realm="api", error="invalid_token"curl --oauth2-bearer TOKEN URL
DigestRFC 7616Digest realm=..., qop="auth", algorithm=SHA-256, nonce=...curl --digest -u user:password URL
Negotiate (Kerberos, SPNEGO)RFC 4559Negotiatecurl --negotiate -u : URL (needs a Kerberos ticket)
curl -v traces against httpbin.org: no credentials, then a wrong password, then a valid one
> GET /basic-auth/user/passwd HTTP/1.1
< HTTP/1.1 401 UNAUTHORIZED
< WWW-Authenticate: Basic realm="Fake Realm"

> Authorization: Basic dXNlcjp3cm9uZw==
< HTTP/1.1 401 UNAUTHORIZED
< WWW-Authenticate: Basic realm="Fake Realm"

> Authorization: Basic dXNlcjpwYXNzd2Q=
< HTTP/1.1 200 OK

04Which layer returned the 401?

Three layers commonly send a 401: the application (a login session or API token check), the web server (password protection such as nginx auth_basic or an Apache .htpasswd), or an authentication proxy or CDN feature in front of both. The page usually gives it away. nginx's built-in page is titled "401 Authorization Required" and arrives with a Basic challenge, while an API answers in JSON with a Bearer challenge.

If the error appears only through the public hostname, compare it with a request straight to the origin using curl --resolve, as described in the status code reference. A 401 that disappears when the request goes straight to the origin points at a reverse proxy or CDN that changed the request on the way.

05Why a 401 appears only behind a proxy or after a redirect

If the credentials are right and the server still says 401, check whether the Authorization header reaches it at all. Three common places lose it.

  • The CDN strips it: Amazon CloudFront removes the Authorization header from GET and HEAD requests before forwarding them, unless the distribution is configured to forward it
  • The web server hides it from the application: Apache keeps Authorization away from CGI and FastCGI scripts, including PHP-FPM behind mod_proxy_fcgi, unless CGIPassAuth On is set (Apache 2.4.13 and later)
  • A redirect drops it: under the Fetch Standard, browsers remove Authorization as soon as a redirect leads to another origin, and curl sends credentials only to the host you first asked unless you use --location-trusted
curl -v -L: the credentials go to the first host only (httpbin.org redirecting to example.com)
> GET /redirect-to?url=https%3A%2F%2Fexample.com%2F HTTP/1.1
> Host: httpbin.org
> Authorization: Basic dXNlcjpwYXNzd2Q=
< HTTP/1.1 302 FOUND
< Location: https://example.com/
> GET / HTTP/1.1
> Host: example.com
(no Authorization header on the second request)

06Can a CDN or proxy cache a 401?

Only when its response headers or a cache rule allow it. A 401 is not cacheable by default, so a cache that follows the standard does not store one on its own. Separately, RFC 9111 says a shared cache must not reuse a response to a request that carried an Authorization header for any later request, unless that response explicitly allows shared caching with public, s-maxage or must-revalidate.

Such rules exist. nginx's proxy cache does not consider the Authorization header unless told to, and proxy_cache_valid any caches every status code. In a test on nginx 1.22.1, a 401 cached from an anonymous request was then served to a request with a valid token, and a private 200 cached from an authenticated request was served to an anonymous one. The configuration below fixed both.

nginx: never answer authenticated requests from the cache, and do not cache 401s (tested with nginx 1.22.1)
# create /var/cache/nginx first if it does not exist; nginx makes only the last level
proxy_cache_path /var/cache/nginx/edge keys_zone=edge:10m;

server {
    listen 80;
    server_name app.example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:8090;
        proxy_cache edge;
        # risky: "proxy_cache_valid any 1m;" would also cache 401, 403 and 500
        proxy_cache_valid 200 301 302 1m;
        proxy_cache_bypass $http_authorization;   # never answer these from cache
        proxy_no_cache $http_authorization;       # never store their responses
    }
}

07401 troubleshooting matrix

SymptomWho sent itLikely causeTestFix
Browser keeps showing a password promptWeb serverWrong user or hash in the password file, or wrong realmcurl -u user:password URLRecreate the entry with htpasswd
API answers 401 with error="invalid_token"ApplicationToken expired or revokedDecode the token and read its exp claimRefresh or reissue the token; check server clocks
401 only through the CDNOrigin, without the headerEdge strips AuthorizationSame request with curl --resolve to the originForward Authorization in the CDN's origin request settings
401 only for PHP on ApacheApplicationAuthorization hidden from scriptsLog the received headersCGIPassAuth On
401 after a redirectApplication on the new hostHeader dropped on a cross-origin redirectcurl -v -L and compare requestsCall the final URL directly
Signed-in users see 401, or strangers see private dataCache401 or private response cachedAge or cache-status headersBypass and skip the cache for Authorization

08How to fix a 401 error as a visitor

Most 401s that people meet in a browser are expired sessions. Work through these in order:

  • Check the URL: an old bookmark can point at an admin or staging area that needs separate credentials
  • Sign out and in again, so the site issues a fresh session cookie
  • Clear the cookies for that one site, or try a private window to see whether stored data is the cause
  • If a browser password prompt appears, the browser remembers what you typed for the rest of the session; after a password change, close the browser completely before trying again
  • If the site keeps refusing a password you know is right, ask its operator; repeated attempts can trigger lockouts or rate limits

Questions

Frequently asked questions

A 401 means the server does not have valid credentials for you: you are not signed in, or the password or token was refused, and it must tell you how to authenticate in a WWW-Authenticate header. A 403 means the server understood the request and refuses it anyway, often because your account lacks permission or a rule blocks the request. New credentials can fix a 401; they rarely fix a 403.

Usually the session is not reaching the server. The cookie may be blocked, expired or set for a different hostname, a token may already be expired because a server clock is wrong, or a proxy may strip the Authorization header. A cache in front of the site can also replay an old 401. Comparing a request through the public hostname with one sent straight to the origin narrows it down.

No. A 401 only says the request did not carry credentials the server accepted at that moment, most often because a session expired. A blocked account or address normally produces a 403 or a page that says so. If a password you know is correct suddenly stops working, change it through the site's reset process and check the account's security settings.

Only over HTTPS. Basic authentication sends the username and password in every request, encoded in base64, which anyone who can read the traffic can decode instantly. RFC 7617 says it is not a secure method unless used with TLS. It remains fine for protecting a staging site or an internal tool over HTTPS, combined with limits on failed attempts.

Pages that answer 401 are not indexed, which is correct for private content. Google treats 4xx codes other than 429 as content that does not exist, and they do not change its crawl rate, so 401 is no way to slow crawlers down. Make sure public pages, images and stylesheets never sit behind authentication by mistake, for example after copying a staging configuration to production.

Sources

Sources and verification

nginx 1.22.1 configurations (Basic authentication, a Bearer stand-in, and a caching proxy with and without the Authorization bypass) were run in an isolated network namespace and exercised with curl 7.88.1; the scheme and redirect traces were captured with curl 7.88.1 against httpbin.org. Debian 12, October 2026.

  1. 01RFC 9110, section 15.5.2: 401 Unauthorized · RFC Editor
  2. 02RFC 9110, section 11.6.1: WWW-Authenticate · RFC Editor
  3. 03RFC 9110, section 15.5.4: 403 Forbidden, and 15.5.8: 407 · RFC Editor
  4. 04RFC 7617, The Basic HTTP Authentication Scheme · RFC Editor
  5. 05RFC 6750, OAuth 2.0 Bearer Token Usage, section 3.1: Error Codes · RFC Editor
  6. 06RFC 7616, HTTP Digest Access Authentication · RFC Editor
  7. 07RFC 4559, SPNEGO-based Kerberos and NTLM HTTP Authentication · RFC Editor
  8. 08RFC 9111, section 3.5: Storing Responses to Authenticated Requests · RFC Editor
  9. 09Fetch Standard, section 4.5: HTTP-redirect fetch · WHATWG
  10. 10curl man page: --location and --location-trusted · curl
  11. 11Apache core: CGIPassAuth directive · Apache HTTP Server
  12. 12Request and response behaviour for custom origins (Authorization header) · AWS
  13. 13Module ngx_http_proxy_module: proxy_cache_valid, proxy_cache_bypass, proxy_no_cache · nginx.org
  14. 14nginx development guide: HTTP phases · nginx.org

Last updated