false
Guides

HTTP 401 Error Explained: Causes and How to Fix It

A graphic of a bar chart with an arrow pointing upward.

The HTTP 401 error is misnamed, and that single fact causes more debugging confusion than almost anything else about it.

The spec calls it "Unauthorized," but it actually means "unauthenticated" — the server doesn't know who you are yet, not that it knows and disagrees with your access level. That distinction determines everything: what caused the error, how to fix it, and what your API should do when it returns one.

This article is for developers, engineers, and technical PMs who are either troubleshooting a 401 in the wild or building APIs that need to return the right status codes. Here's what you'll learn:

  • What HTTP 401 actually means — and why the official name misleads even experienced engineers
  • The most common causes, from expired tokens to stale browser cookies to OAuth-specific token failures
  • How 401 and 403 differ, and why mixing them up breaks API clients in specific, hard-to-trace ways
  • Step-by-step fixes for both end users and developers, including an infrastructure gotcha with AWS CloudFront
  • How to emit 401 correctly in your own API design so clients can handle it reliably

The article moves from definition to diagnosis to fixes to design — so whether you're stuck on a specific error right now or building the system that returns these codes, you can jump to the section that fits your situation.

What the HTTP 401 status code actually means

Before you can diagnose or fix a 401, you need a precise definition — and the official name of this status code actively works against you. "401 Unauthorized" is what the spec calls it, but what it actually means is closer to "unauthenticated."

MDN's documentation acknowledges this directly: "Although the HTTP standard specifies 'unauthorized', semantically this response means 'unauthenticated'." That single naming quirk is responsible for a surprising amount of confusion, even among experienced engineers.

The Hacker News community has flagged it repeatedly — one developer noted they need to "pause for a few hundred milliseconds just to be sure I'm using the right one in a sentence." If that resonates, this section is for you.

Authentication failure, not a permissions problem

The precise definition from MDN: a 401 indicates "a request was not successful because it lacks valid authentication credentials for the requested resource." The operative concept is identity, not permission. The server isn't saying it knows who you are and has decided to block you — it's saying it cannot determine who you are at all. SuperTokens frames it cleanly: "the server has failed to identify the user."

This distinction matters because it determines what corrective action is even possible. A 401 is fundamentally about the who, not the what. You haven't been denied access to a resource; you haven't been recognized as anyone yet. The server is waiting for you to prove your identity before it will evaluate anything else.

A useful three-way framing: 401 means "I don't know who you are." 403 means "I know who you are, but you can't come in." 404 means "I have no idea what you're looking for." These aren't interchangeable states — they require different responses from the client and signal different conditions in your system.

The WWW-Authenticate header: the server's instructions for recovery

What separates a 401 from a generic failure is a required companion: the WWW-Authenticate response header. Per the HTTP specification, when a server returns 401, it must include this header, which tells the client exactly which authentication scheme it needs to use to succeed on a retry.

A concrete example from MDN: a GET /admin request without credentials returns HTTP/1.1 401 Unauthorized alongside WWW-Authenticate: Bearer. That header isn't incidental — it's the server's explicit instruction that Bearer token authentication is required. The client now knows not just that the request failed, but precisely how to fix it.

This is what makes 401 a structured, actionable error rather than a dead end. The server isn't just rejecting the request; it's providing a recovery path. Basic, Bearer, Digest, and others are all valid values — and each tells the client something specific about what credentials to supply.

Why 401 is temporary and correctable

Because a 401 signals missing or invalid credentials rather than a permanent access denial, it describes a state the client can resolve. Authenticate correctly — supply the right token, the right credentials, the right scheme — and the server can evaluate the request on its merits.

This is the critical contrast with 403. A 403 response means the server has successfully authenticated the requester and has still decided to deny access. There is no credential the client can supply to change that outcome. Reauthenticating won't help; the problem isn't identity, it's authorization policy.

The presence of the WWW-Authenticate header reinforces this: it's the server's invitation to try again with proper credentials. A 403 carries no such invitation, because there's nothing to try again with.

For developers and technical PMs building or debugging systems, this distinction isn't academic. Treating a 401 as a 403 — or vice versa — produces real failures: an API client that receives a 403 but thinks it's a 401 will keep refreshing its token and retrying, never realizing that the problem is permissions, not credentials.

An API client that receives a 401 but treats it like a 403 will give up immediately on a request it could have fixed with a fresh login. The rest of this article builds on this foundation, so it's worth holding the definition precisely: 401 means the server cannot identify you, and it has told you how to fix that.

Four reasons a server returns 401 — and which context each belongs to

A 401 error has a specific meaning — the server doesn't know who you are — but the reasons that situation arises vary considerably depending on whether you're a user hitting a login wall or an engineer debugging an API integration.

The four cause categories below map to those two contexts: the first three tend to surface in browser-based sessions, while the fourth is almost exclusively a developer-facing problem in API and OAuth flows. Identifying which category fits your situation is the fastest path to the right fix.

Missing or incorrect credentials

The most straightforward cause: the request arrived at the server without valid credentials, either because none were provided at all or because the ones provided were wrong. For a human user, this is a mistyped password or a username that doesn't match any account. For a programmatic client, it's an API request that went out without an Authorization header entirely.

MDN's canonical example illustrates this cleanly: a GET /admin request that includes no Authorization header receives a 401 Unauthorized response with a WWW-Authenticate: Bearer header in reply. The server isn't saying you're forbidden — it's saying it has no idea who's asking.

The fix in this case is straightforward — provide the right credentials — but you first have to confirm that credentials are actually missing rather than present but invalid.

Expired tokens or sessions

This cause is distinct from wrong credentials because authentication succeeded at some point. The user or client authenticated correctly, received a valid credential, and then that credential aged out. A session cookie that timed out after inactivity, or a JWT whose exp claim has passed, will both produce a 401 on the next request even though the underlying identity is perfectly valid.

The server isn't rejecting the identity — it's rejecting the credential's current state. This distinction matters for debugging: if you're seeing a 401 that appears intermittently or only after a period of inactivity, an expired token or session is the most likely culprit.

For API clients using short-lived access tokens, this is a routine operational condition that should be handled programmatically with a token refresh flow rather than treated as an error requiring human intervention.

Stale browser cache and cookies

A browser can produce a 401 by sending authentication cookies from a previous session that are no longer recognized by the server. This is related to but distinct from token expiry: the issue here is the browser's stored state, not the token's own expiration timestamp.

A server-side session invalidation — a forced logout, a password reset, or a backend session store flush — can leave the browser holding cookies that the server no longer considers valid.

Users who clear their cache and cookies and find the 401 disappears were almost certainly in this situation. The credential wasn't wrong and it wasn't expired on its own terms — the server simply stopped recognizing it, and the browser kept sending it anyway.

API and OAuth-specific token issues

For engineers working with API integrations, the 401 cause landscape expands. Beyond a missing Authorization header, common triggers include a revoked OAuth access token, a token issued for the wrong scope or audience, or a token that was valid when the integration was built but has since been invalidated by an authorization server event — a user revoking app access, an admin rotating credentials, or a connected application being deauthorized.

A revoked OAuth token is particularly worth calling out because it can look identical to an expired token from the client's perspective. The token hasn't hit its expiration timestamp, but the authorization server has marked it invalid. The WWW-Authenticate: Bearer response header the server returns is the signal that a Bearer token is required — or that the one provided is no longer accepted. In these cases, the fix isn't a simple retry; it requires obtaining a new token through the authorization flow.

Infrastructure-level 401s are also worth acknowledging, even if they're less common. Intermittent 401s on services using Windows Authentication in IIS, for example, can appear even when credentials are valid — a reminder that not every 401 is a straightforward credential problem, and that the server's authentication stack itself can be a variable.

HTTP 401 vs. 403: why the wrong code breaks client behavior

Before you can reliably troubleshoot a 401 or design an API that returns the right error code, you need to understand what separates these two responses at a fundamental level. They're not interchangeable, and treating them as roughly equivalent is one of the more consequential mistakes in API design.

401 and 403 represent different points in the access control pipeline

A 401 and a 403 represent different points in the access control pipeline — authentication and authorization — and conflating them means conflating two distinct server states.

When a server returns a 401, it's saying it cannot identify the requester. The request arrived without valid credentials, with credentials that couldn't be verified, or with no credentials at all. As Permit.io puts it, the server is essentially saying: "I don't recognize you." A 403, by contrast, is returned after the server has successfully identified the requester — and is still refusing access. The user is known; they just don't have permission to touch that resource.

A useful analogy: a 401 is like showing up to a locked building with no key at all. A 403 is like having a key that works on the front door but not on the server room. In the first case, the problem is that you haven't established who you are. In the second, your identity is established — you're just not allowed in.

There's a naming trap worth calling out directly. The 401 status code is labeled "Unauthorized," which misleads a surprising number of developers into treating it as a permissions error. It isn't. The label is a historical artifact. The code is strictly an authentication signal — the server doesn't know who you are, not that it knows and disagrees with your access level. "Unauthenticated" would be more accurate, and that's the mental model you should carry.

What each code tells the client to do next

The distinction isn't just semantic — it has direct implications for how clients should respond.

A 401 response must include a WWW-Authenticate header (per RFC 9110), which is an explicit protocol-level instruction: provide valid credentials and try again. This is what makes a 401 a correctable, temporary state. The server is not slamming the door permanently; it's telling the client exactly what it needs to do to proceed.

A 403 carries no such instruction, because reauthentication won't help. The user's identity is already established. The problem is permissions, not credentials. A client that retries with fresh tokens after receiving a 403 is wasting requests — it will keep getting the same answer.

A client that gives up after a 401 is failing to recover from a state that was always fixable. The WWW-Authenticate header is the structural, spec-level signal that separates these two codes — not just a matter of convention.

Why conflating 401 and 403 breaks API design

If an API returns 403 when it should return 401 — because the developer conflated "unauthorized" with "forbidden" — clients cannot implement reliable token refresh or reauthentication flows. They interpret a fixable authentication failure as a permanent permission denial and stop retrying. The user gets locked out of something they should be able to access, and the client has no signal to trigger a login prompt or token refresh.

The reverse mistake is equally damaging. Returning 401 when the user is authenticated but lacks permissions sends clients into reauthentication loops that will never succeed, because the problem isn't credentials — it's access control policy.

The distinction is well-understood among practitioners who work with API design regularly — the confusion almost always originates on the implementation side, where developers reach for the more familiar-sounding "Unauthorized" label without reading what the spec actually requires.

The bottom line: use 401 when the request lacks valid authentication, and use 403 when the authenticated user doesn't have the required permissions. The codes are not interchangeable, and getting this right is the foundation of a predictable API contract.

Fixing a 401 depends on where in the stack the credential broke down

The fix for a 401 always traces back to the same root cause: the server did not receive valid authentication credentials. What changes is where that breakdown happened — in a browser session, an API request, or somewhere in the infrastructure pipeline between the client and the origin server. Knowing which context you're in determines which fix applies.

Fixes for end users

If you're hitting a 401 in a browser, the server is essentially saying it doesn't know who you are. The path forward is to re-establish your identity through one of three actions.

First, verify that your credentials are actually correct. A mistyped password or an email entered under the wrong account is the most common cause and the easiest to rule out. Second, clear your browser cache and cookies. Stale session data can cause your browser to send an expired or invalidated token on your behalf — the server rejects it, and you see a 401 even though you believe you're logged in. Third, log out completely and reauthenticate. A fresh login generates a new session token or cookie, which gives the server the valid proof of identity it's looking for.

These three steps resolve the majority of browser-side 401s. If you've done all three and the error persists, the problem is likely on the server or infrastructure side — which is where developers need to take over.

Fixes for developers: confirming the Authorization header is transmitted

For developers, the diagnostic starting point is the Authorization header. According to the MDN specification, when a server returns a 401, it includes a WWW-Authenticate header in the response that tells the client exactly what authentication scheme is expected — for example, WWW-Authenticate: Bearer signals that the server expects a Bearer token in the Authorization header of the next request. If that header is missing, malformed, or carrying an expired token, the server will reject the request.

The critical mistake developers make is assuming the Authorization header is being sent just because it was set in application code. A real-world example from Stack Overflow illustrates this well: an ASP.NET web service using Windows Authentication in IIS6 (Microsoft's web server software) was returning intermittent 401 responses even when valid Active Directory credentials were passed.

The issue was infrastructure-layer — credentials set at the application level weren't reliably forwarded through the underlying server stack. The fix required verifying what was actually being transmitted at the wire level, not just what the application assumed it was sending.

The practical implication: use a tool like curl, Postman, or your browser's DevTools Network tab to inspect the actual outbound request. Confirm the Authorization header is present in the request that reaches the server, not just in your application configuration.

The AWS CloudFront case

One infrastructure gotcha worth knowing: AWS CloudFront strips the Authorization header from requests by default before forwarding them to your origin server. This means a correctly configured application can still generate 401 errors at the origin simply because CloudFront is silently dropping the credential.

The fix is to explicitly configure a cache policy or origin request policy in CloudFront to forward the Authorization header to the origin. If you're running into persistent 401s behind a CloudFront distribution and your application-level auth looks correct, this is the first place to check in your AWS configuration.

Confirming the 401 is resolved: what a successful response looks like

Once you've applied a fix, confirmation is straightforward. A successful resolution means the server returns a 2xx response instead of a 401. For developers, also check that the WWW-Authenticate header is no longer present in the response — its presence is the server's signal that authentication is still required and the fix hasn't taken effect yet.

For API integrations specifically, confirm that the authentication scheme in your Authorization header matches what the server specified in its WWW-Authenticate response. Sending Authorization: Basic credentials to a server expecting Bearer tokens will produce a 401 even if the credentials themselves are valid. The server's WWW-Authenticate header is the authoritative source of truth for what it expects — read it before assuming the credential format is correct.

HTTP 401 in API design: returning the right status code

How you emit a 401 matters as much as how you handle one. For API designers and backend engineers, the status codes your API returns are part of its contract — and getting 401 wrong doesn't just create confusion, it breaks the automated systems that depend on it.

The spec-based case for using 401 correctly

The HTTP specification is unambiguous: a 401 response means the request lacks valid authentication credentials. It does not mean the client is forbidden from a resource, and it does not mean "access denied" in a general sense. That distinction belongs to 403.

A correctly formed 401 response includes a WWW-Authenticate header. This isn't optional. The header tells the client what authentication scheme the server expects, giving the client everything it needs to retry with proper credentials. A minimal compliant response looks like this:

HTTP/1.1 401 Unauthorized
Date: Tue, 02 Jul 2024 12:18:47 GMT
WWW-Authenticate: Bearer

Many production APIs return 401 without the WWW-Authenticate header. That's a spec violation — and it has real consequences. Auth libraries, proxies, and API clients rely on that header to determine how to authenticate. Without it, the client receives a signal that says "try again with credentials" but no information about what credentials to provide or in what format.

The retry expectation baked into 401: what clients are supposed to do

The reason 401 is described as a correctable, temporary state is that the protocol expects a retry. When a client receives a 401, the intended flow is: inspect the WWW-Authenticate header, obtain or refresh valid credentials, and resubmit the request. This is the mechanism that powers token refresh flows in OAuth — the 401 is the trigger that tells the client its access token has expired and needs to be replaced before retrying.

This retry expectation is built into SDKs, API clients, and CI pipelines. It's also increasingly relevant in AI-based automation tools that chain multiple API calls together — these systems read status codes and decide automatically whether to retry, stop, or escalate. A 401 tells them to reauthenticate and try again. A 403 tells them to stop entirely. These are fundamentally different instructions, and automated systems follow them literally — they don't read error messages, they read codes.

The downstream cost of misusing 401 is significant. Returning 401 when the correct response is 403 can trigger infinite retry loops or token refresh storms — the client keeps refreshing its token and retrying, never realizing that the problem isn't authentication at all.

These failures are also difficult to trace in observability tooling, because monitoring systems classify 401s and 403s differently. A flood of 401s looks like an authentication outage; a 403 signals a permissions problem. Misclassification creates noise that obscures the real issue.

API design patterns that corrupt the 401 signal

Several patterns recur in API design that undermine the reliability of 401 as a signal.

The most common is returning 401 when 403 is correct. If the client has authenticated successfully but lacks permission to perform the action, that's a 403 — not a 401. Returning 401 in this case tells the client to reauthenticate, which won't help, because the problem is authorization, not authentication.

A related mistake is using 401 as a generic "access denied" catch-all. Some APIs return 401 for any request that doesn't succeed for security-related reasons, regardless of whether authentication is actually the issue. This collapses meaningful distinctions that clients need to handle errors correctly.

Omitting the WWW-Authenticate header, as noted above, is a spec violation that breaks auth libraries and proxies even when the status code itself is correct.

Finally, there's the anti-pattern of returning 200 OK with an error payload instead of a proper 4xx code. This breaks all programmatic error handling — clients that check status codes before parsing response bodies will treat the request as successful and potentially propagate the error silently.

The recommendation here is straightforward: use 401 when and only when the request lacks valid authentication credentials, always include the WWW-Authenticate header, and reserve 403 for permission failures. APIs that follow this contract give their consumers — whether human developers or automated agents — the information they need to respond correctly.

Diagnosing a 401: the distinction that separates a fast fix from a long debug

The through-line of this entire article is a single, precise distinction: a 401 means the server cannot identify you, not that it has identified you and said no. That's a 403. Holding that distinction clearly is what separates a fast diagnosis from a long debugging session — and what separates a well-designed API from one that sends its consumers into retry loops they can never escape.

Quick diagnostic: is it an authentication problem or a permissions problem?

The fastest question to ask when you see a 401 is: has this request ever succeeded with these credentials? If yes, the credential has likely expired, been revoked, or been dropped somewhere in the infrastructure layer — none of which are permissions problems.

If the request has never succeeded, start with whether valid credentials were sent at all, and read the WWW-Authenticate header to confirm what the server actually expects.

Resolution path for users and developers

For end users, the sequence is straightforward: verify your credentials are correct, clear your browser cache and cookies, and reauthenticate with a fresh login. For developers, the discipline is to inspect what's actually transmitted at the wire level — not what your application assumes it's sending.

A curl request or a DevTools network trace will tell you whether the Authorization header is present, correctly formatted, and carrying a token that matches the scheme the server specified. If those check out and the 401 persists, the problem is likely upstream of your application code.

When to escalate: infrastructure, proxy, and API gateway issues

The AWS CloudFront case is the canonical example, but it's not the only one: any proxy, CDN, or API gateway sitting between your client and origin server is a candidate for silently stripping or transforming the Authorization header.

If your application-level auth looks correct and you're still getting 401s, the next step is to test a direct request to the origin, bypassing the intermediary entirely. If that succeeds, the problem is in the layer between — and the fix is a configuration change, not a code change.

Where to start, whether you're debugging or designing

The tension worth keeping in mind: precision in error codes feels like a small thing until it isn't. A 401 returned where a 403 belongs will eventually produce a token refresh storm or a silent retry loop in an automated system, and those failures are genuinely hard to trace. The cost of getting this right is low; the cost of getting it wrong compounds quietly over time.

Start with where you are:

If you're a user seeing a 401 in a browser: Clear your cache and cookies, log out, and reauthenticate. If the error persists after a fresh login, the problem is on the server side — contact support or your IT team.

If you're a developer debugging a live 401: Open your network inspector and check two things: (1) Is the Authorization header present in the outbound request? (2) Does the scheme in your header match what the server specified in WWW-Authenticate? If both check out and the 401 persists, test a direct request to the origin to rule out a proxy or CDN stripping the header.

If you're an API designer auditing your error responses: Find every place you return a 401 and ask: has the client authenticated? If yes, the correct code is 403, not 401. Find every 401 that lacks a WWW-Authenticate header and add one — it's required by the spec and expected by auth libraries.

If you're building APIs that gate access to features or experiments, GrowthBook's feature flagging and experimentation platform relies on clean authentication signals from your backend — a correctly formed 401 with a WWW-Authenticate header is what allows token refresh flows to complete reliably before flag evaluations run.

This article was written to be genuinely useful whether you're staring at a 401 right now or designing the system that returns them — and if it helped you get unstuck or think more clearly about the problem, that's exactly what it was for.

Related insights

Table of Contents

Related Articles

See All articles
Experiments

Best 7 A/B Testing tools with Product Analytics

May 8, 2026
x
min read
Experiments

Best 7 Warehouse Native A/B Testing Tools

May 5, 2026
x
min read
Analytics

How to Track Unique Visitors on Your Website

May 4, 2026
x
min read

Ready to ship faster?

No credit card required. Start with feature flags, experimentation, and product analytics—free.

Simplified white illustration of a right angle ruler or carpenter's square tool.White checkmark symbol with a scattered pixelated effect around its edges on a transparent background.