BugMojoBugMojoBugMojo
FeaturesPricingBlogHelpAbout
Add to ChromeLog inGet started
BugMojoBugMojo

Bug reports that actually help fix bugs — capture, replay, share.

A product of Softech Infra.

Product

  • Features
  • Pricing
  • Browser extension
  • Get started
  • Log in

Resources

  • Help & guides
  • Blog
  • Compare
  • Glossary

Company

  • About
  • Contact
  • Security
  • Privacy
  • Terms
  • Sitemap
© 2026 BugMojo. All rights reserved.
AllGuidesEngineeringPlaybooksCompareGlossaryAlternativesBy roleBug tracking by framework
  1. Home
  2. Blog
  3. Glossary
  4. 401 Unauthorized vs 403 Forbidden: What's the Difference?
Glossary

401 Unauthorized vs 403 Forbidden: What's the Difference?

A 401 means the server does not know who you are — authenticate and try again. A 403 means it knows exactly who you are and is refusing anyway. Here is the difference in one table, plus the fix for each.

Hrishikesh BaidyaHrishikesh Baidya·Jul 16, 2026·9 min read
Glossary
Isometric line-art comparison of a 401 Unauthorized panel and a 403 Forbidden panel meeting at a central auth gate, lime on dark charcoal
TL;DR
  • 401 Unauthorized = you are not authenticated. The server does not know who you are — your credentials are missing, expired, or invalid. Log in (or send a valid token) and retry.
  • 403 Forbidden = you are authenticated but not authorized. The server knows exactly who you are and is refusing anyway. Logging in again will not help.
  • The one-liner: 401 is an authentication problem; 403 is an authorization problem.
  • A correct 401 must carry a WWW-Authenticate header telling the client how to authenticate. A 403 carries no such invitation — there is nothing to retry.

These two status codes get swapped constantly, and the confusion is understandable — both are 4xx client errors, both look like a door slamming in your face, and the name of the first one actively lies to you. But they describe two completely different failures, and knowing which one you are looking at tells you exactly what to do next. So here is the answer first, then the detail.

401 Unauthorized means: "I don't know who you are." Your request arrived with no credentials, or with credentials the server could not accept — a missing token, an expired session, a wrong API key. The server is asking you to identify yourself and try again.

403 Forbidden means: "I know exactly who you are, and you still can't have this." Your credentials were valid and the server identified you successfully. It then checked what you are allowed to do and decided the answer is no. Re-authenticating changes nothing, because authentication was never the problem.

The one-line answer: authentication vs authorization

401 Unauthorized is an authentication failure: the server cannot verify who you are, so you must supply valid credentials and retry. 403 Forbidden is an authorization failure: the server already knows who you are but refuses to grant access. In short — 401 asks you to log in; 403 tells you that logging in will not help.

Everything about these two codes reduces to two words that are easy to blur together:

  • Authentication — who are you? Proving identity. Handled by a login, a session cookie, a bearer token, or an API key.
  • Authorization — what are you allowed to do? Checking permissions after identity is established. Handled by roles, scopes, ownership, and policy rules.

A request flows through them in order. First the server authenticates you; if that fails, you get a 401 and the pipeline stops there — there is no point checking permissions for someone the server cannot even identify. Only once you are authenticated does the server check authorization; if that fails, you get a 403. That ordering is the whole model: 401 is the earlier gate, 403 is the later one.

Why 401 is named 'Unauthorized' but means unauthenticated

Here is the single most confusing fact in this whole topic: the status code literally called "Unauthorized" is the one about authentication. Read that again, because it trips up experienced engineers.

The name is a historical artifact. HTTP was standardized long before the industry settled on the crisp authentication-versus-authorization vocabulary we use now. The current specification, RFC 9110, is careful about it — it defines 401 as the response for a request that "lacks valid authentication credentials for the target resource." That is unambiguously an authentication statement. But the human-readable label attached to the code has always been "Unauthorized," and changing it now would break decades of tooling.

So the practical rule is: whenever you see 401 Unauthorized, mentally translate it to "401 Unauthenticated." The 403 keeps its honest name — 403 Forbidden really does mean forbidden, as in "you are known and still not permitted." If you internalize that one swap, the two codes stop blurring.

The WWW-Authenticate header a proper 401 must send

There is a formal difference between the two codes beyond intent, and it lives in the response headers. Per the spec, a 401 response MUST include a WWW-Authenticate header. That header is the server's way of saying not just "authenticate yourself" but "here is how to authenticate" — it names the scheme (Basic, Bearer, Digest) and an optional realm. It is an invitation: the client can read it, gather credentials, and retry the exact same request.

401-response.httphttp
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token"
Content-Type: application/json

{ "error": "The access token has expired" }

A 403 sends no such header, and that absence is meaningful. There is nothing to negotiate. The server is not asking for different credentials — it has your credentials, they are fine, and the answer is still no. The client cannot fix a 403 by re-sending the request with a fresh token, because the request was never rejected for lack of a token. This header difference is the machine-readable version of the whole distinction: a 401 says "try again like this," a 403 says "don't bother."

401 vs 403 side by side

Feature401 Unauthorized403 Forbidden
What it meansI don't know who you areI know who you are — access still denied
Are you authenticated?No — credentials missing/invalidYes — identity accepted
Will logging in help?✓—
Typical causeMissing/expired token, wrong API key, not logged inInsufficient role/permission, IP or geo block, not your resource
The fixSend valid credentials, then retryGet permission granted, or use an allowed account
WWW-Authenticate headerRequiredNot sent — nothing to retry
The two codes reduced to six questions. Every row is a different answer.

When you actually get a 401

In real applications a 401 almost always traces back to a credential that is absent or no longer good:

  • Missing or expired token. The most common case in single-page apps and APIs. Your access token has a short lifetime; once it expires, every request comes back 401 until you refresh the token or log in again.
  • Wrong or revoked API key. A typo in the key, a key for the wrong environment, or a key an admin rotated will all fail authentication.
  • Not logged in at all. Hitting a protected endpoint before any session exists — no cookie, no Authorization header — is the textbook 401.
  • Malformed credentials. A Bearer token that got truncated, an incorrectly encoded Basic auth string, or a clock-skewed signed request the server cannot verify.

The common thread: fix the credential and the same request succeeds. That is the signature of a 401.

When you actually get a 403

A 403 shows up when you are logged in perfectly well and the server still says no. The usual causes:

  • Insufficient role or permission. A standard user hitting an admin-only endpoint. Authentication succeeded; the authorization check for that route failed.
  • Accessing a resource you don't own. Requesting /invoices/842 when invoice 842 belongs to another account. You are authenticated, just not entitled to that specific record.
  • IP or geo blocking. A firewall, WAF, or region restriction refuses the request regardless of who you are.
  • CSRF or referer checks. A state-changing request that arrives without a valid CSRF token or from an unexpected origin/referer is often rejected with 403 as a defense-in-depth measure.
  • Rate or policy rules. Some servers use 403 for requests that violate a usage policy even though the caller is a valid, known client.
The 404-instead-of-403 trick

Sometimes a server that should return 403 deliberately returns 404 Not Found instead. Why? A 403 confirms that the resource exists — you are just not allowed to see it. For a private repository, a hidden admin page, or another user's record, that confirmation is itself an information leak. Returning 404 hides existence entirely: the client cannot tell "this is here but off-limits" from "this was never here." RFC 9110 explicitly permits this, and GitHub is a well-known example — private resources you cannot access report 404, not 403.

How to fix a 401

Because a 401 is always about credentials, the fixes all involve producing valid ones:

  • Log in again to establish a fresh session, or run your token-refresh flow to swap an expired access token for a new one.
  • Check the Authorization header is actually being sent. A surprising number of 401s are the client simply not attaching the token — the request leaves without it.
  • Verify the credential is correct for the environment — right API key, right scheme (Bearer vs Basic), no stray whitespace or truncation.
  • Read the WWW-Authenticate header in the response; it tells you which scheme and realm the server expects, and often an error hint like invalid_token.

How to fix a 403

A 403 cannot be fixed by the client alone in most cases, because the problem is policy, not credentials:

  • Get the permission granted. Ask an admin to add the required role, scope, or group membership to your account. This is the real fix for the most common 403.
  • Use an account that is actually allowed. If the resource belongs to someone else, switch to the owning account or request sharing — re-logging into the same account will keep failing.
  • Check for a CSRF/referer or origin rule if the 403 hits a state-changing request; make sure the token is included and the origin is expected.
  • Look for an IP, geo, or WAF block if it is infrastructure-level; that fix lives in the firewall or CDN config, not the app.

If you are the API author and you find yourself returning 403 for an expired session, that is a bug — an expired credential is a 401, and mislabeling it sends clients down the wrong recovery path.

Telling them apart fast: look at the request that was sent

The quickest way to disambiguate 401 from 403 in practice is not to guess from the status code alone — it is to look at what the failing request actually sent. Open the request in your browser DevTools Network panel (or a captured report) and check one thing: was a valid Authorization header or session cookie present?

  • No auth header, or an obviously expired token, plus a 401 → an authentication problem. Refresh or log in.
  • A perfectly good auth header plus a 403 → an authorization problem. The token is fine; the account lacks permission.

This is exactly the kind of detail that evaporates in a hand-written bug report. "The page says forbidden" tells you nothing about whether a token was attached. This is where a capture tool earns its keep: BugMojo records the real network request behind the failure, so the filed bug shows whether an auth header was actually sent, what its expiry was, and the exact response — instantly separating a 401 (no or expired token) from a 403 (token present but not allowed) without a back-and-forth.

Both of these are members of the wider 4xx family. If you want the full map of status codes and where 401 and 403 sit among their siblings, see HTTP status codes explained. And because auth failures are frequently confused with cross-origin failures — a blocked preflight can look like a permissions problem when it is really CORS — the companion read is how to debug CORS errors, which untangles the two.

Stop guessing 401 vs 403 from the status line alone.

Install the free BugMojo extension and every captured bug arrives with the real network request attached — headers, token state, and response — so authentication and authorization failures tell themselves apart.

Install the extension

Frequently asked questions

Frequently asked questions

Sources

  1. 401 Unauthorized — the request lacks valid authentication credentials — MDN Web Docs (2026)
  2. 403 Forbidden — the server understood the request but refuses to authorize it — MDN Web Docs (2026)
  3. WWW-Authenticate header — defines the authentication method for accessing a resource, sent with a 401 — MDN Web Docs (2026)
  4. RFC 9110 HTTP Semantics — 401 lacks valid authentication credentials; 403 the server refuses to authorize; 404 may be used to hide a forbidden resource — IETF RFC 9110 (2022)
Share:
Hrishikesh Baidya
Hrishikesh Baidya· Chief Technology Officer

Hrishikesh Baidya is the CTO at Softech Infra. He is drawn to architecture that is invisible — systems that simply work — and leads the engineering behind BugMojo.

On this page

  • The one-line answer: authentication vs authorization
  • Why 401 is named 'Unauthorized' but means unauthenticated
  • The WWW-Authenticate header a proper 401 must send
  • 401 vs 403 side by side
  • When you actually get a 401
  • When you actually get a 403
  • How to fix a 401
  • How to fix a 403
  • Telling them apart fast: look at the request that was sent

Get bug-tracking insights, weekly.

Engineering deep-dives, QA playbooks, and honest tool comparisons. No spam — unsubscribe in one click.