401 vs 403: what the difference means for your app
Two codes people use interchangeably. The choice quietly tells attackers what exists.
What it means
401 Unauthorized means the request carried no valid credentials — the server does not know who is asking. 403 Forbidden means the server knows exactly who is asking and is refusing anyway. The names are historically confusing: 401 is really 'unauthenticated'.
Why it happens in AI-built apps
- Frameworks return whichever their default is, so the two get used interchangeably.
- AI-generated middleware often returns 403 for missing sessions, when 401 is correct.
- A 401 sent without a WWW-Authenticate header is technically incomplete, though rarely a practical problem.
Is this error actually a problem?
Check it yourself
Check what your app returns in each case
- 1Signed out, request a protected API route. You should get 401.
- 2Signed in as an ordinary user, request an admin route. You should get 403 or 404.
- 3Signed in as user A, request a record belonging to user B by changing the id. You should get 404 — a 403 confirms the record exists.
- 4Anything that returns 200 in those last two cases is the real bug, and it is far more serious than the status code being inconsistent.
Fix it
Copy this into your AI coding tool
Review how my app returns 401, 403 and 404, and make it consistent.
1. List every place the app rejects a request, and the status code used.
2. Apply this rule:
- no session or an invalid one → 401
- valid session, but the caller may not perform this action on a
resource they already know about → 403
- the resource exists but belongs to someone else → 404, so its
existence is not confirmed
3. Flag any endpoint that returns 200 where it should reject — that is the
actual vulnerability, and it is easy to miss while tidying status codes.
4. Make sure error bodies do not leak details: no stack traces, no
database messages, no internal identifiers.
Give me a table of endpoint, condition, and status code before and after.This is one check out of 40+
Paste your site address and we run the whole list from the outside — leaked keys, open databases, unprotected pages — then hand you one prompt that fixes what we find. Free, about 30 seconds, no signup.
Check my site — free