---
title: 401 vs 403: Unauthorized vs Forbidden
description: 401 and 403 are the two 'you can't have this' codes — and they're constantly confused. Here's the simple rule for telling them apart, with examples.
canonical: https://watchfor.io/blog/401-vs-403
---

# 401 vs 403: Unauthorized vs Forbidden

2026-03-08 · WatchFor Team · Networking · http, basics, api

401 and 403 are the two 'you can't have this' codes — and they're constantly confused. Here's the simple rule for telling them apart, with examples.

Two HTTP codes mean "access denied," and developers mix them up daily: **401 Unauthorized** and **403 Forbidden**. Using the wrong one confuses clients and makes debugging harder. The good news: one short rule sorts them out forever.

## The one-rule difference

It comes down to **identity**:

> **401 = "I don't know who you are."** (Authenticate first.)
> **403 = "I know who you are — and you still can't."** (Permission denied.)

A 401 is about *authentication* (proving who you are). A 403 is about *authorization* (what you're allowed to do once you're known). And here's a naming irony worth noting: the code literally named "Unauthorized" (401) is actually about *authentication*, not authorization.

## Side by side

| | 401 Unauthorized | 403 Forbidden |
| --- | --- | --- |
| **Means** | Not authenticated | Authenticated, but not permitted |
| **The question** | "Who are you?" | "Are you allowed?" |
| **Fix** | Log in / send valid credentials | Get the right permissions |
| **Will retrying with login help?** | ✅ Yes | ❌ No |
| **Typical trigger** | Missing/expired token | Valid token, insufficient role |

## A quick analogy

Think of a members-only club:

- **401** — you're at the door with no membership card. "Show your card." Once you do, you're in.
- **403** — you're a member, card and all, but you're trying to enter the *staff-only* room. Your membership is valid; you're just not allowed *there*. Showing your card again changes nothing.

## Examples

| Scenario | Code |
| --- | --- |
| Calling an API with no auth token | **401** |
| Token expired | **401** |
| Logged-in user opening another user's data | **403** |
| A "member" hitting an admin-only endpoint | **403** |
| Valid login, wrong account/tenant | **403** |

## Why getting it right matters

For **APIs**, the distinction is a contract. A client that gets a 401 should try to **refresh its token and retry**; a client that gets a 403 should **not** retry — it'll just fail again. Returning the wrong code sends clients into pointless retry loops or makes them give up when a re-auth would've worked.

It also matters for [monitoring](/blog/api-monitoring-guide): on a protected endpoint, a **401 or 403 might be the expected, healthy response** — so configure checks to treat it as such, and only alert when the code is *wrong* for that endpoint.

## The bottom line

| | In one line |
| --- | --- |
| **401** | Not authenticated — "log in." |
| **403** | Authenticated but not permitted — "you can't, even logged in." |
| **Rule** | 401 = who are you; 403 = are you allowed. |
| **APIs** | Retry after re-auth on 401; don't retry on 403. |

Remember the club: 401 is the front door, 403 is the staff room. Get the code right and your APIs (and your debugging) get a lot clearer.

Deep dives: [403 Forbidden](/blog/403-forbidden) and [HTTP status codes explained](/blog/http-status-codes-explained).

---

Canonical page: https://watchfor.io/blog/401-vs-403 · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
