401 vs 403: What Each Error Means and How to Fix Both
What Do These Errors Mean?
Both errors live in the same HTTP family and both involve access control, but the diagnosis is completely different:
- 401 Unauthorized — the server doesn't know who you are. You sent no credentials, or the credentials were invalid. Authentication failed.
- 403 Forbidden — the server knows who you are (or you're anonymous and that's the point), but you don't have permission for this resource. Authorization failed.
The memory trick: 401 = identity problem, 403 = permission problem. Fixing the wrong one wastes hours — I've seen teams add roles to fix a token-expiry bug.
How to Diagnose Which You're Dealing With
401 symptoms: the error appears after a session timeout, a token rotation, or when you curl without headers. Fix the auth layer: check that the Authorization header is present, the token isn't expired, and the secret used to verify matches the one that signed it.
curl -i https://api.example.com/data
# 401 → no token sent
curl -i -H "Authorization: Bearer $TOKEN" https://api.example.com/data
# still 401 → token invalid/expired, or wrong secret403 symptoms: you ARE authenticated (the token validates fine) but the resource still refuses. Check roles, permissions, IP allowlists, and CORS origin rules (a 403 on the OPTIONS preflight is almost always CORS).
Common Causes of 401
- Token expired — JWTs are commonly 15min-1hr; add refresh handling
- Wrong header format —
Authorization: Bearermissing the word Bearer, or Basic vs Bearer confusion - Signature mismatch — the server verifying with a different secret than the issuer signed with (env var drift between services)
- Missing cookie in the request — cross-origin requests need
credentials: 'include'
Common Causes of 403
- User role doesn't include the required permission — check the RBAC mapping
- IP allowlist rejects the request — check VPN/proxy/region rules
- Resource-level restrictions — file permissions, S3 bucket policies, folder ACLs
- WAF/firewall blocking the pattern — look for bot-detection or rate-limit false positives
- CSRF token missing/invalid — common on POST endpoints returning 403
The 5-Minute Debug Sequence
1. Curl the endpoint with verbose output (curl -v) — see exactly which header/middleware responds.
2. Test with a known-good token (issue one manually in dev, not via the login flow).
3. Check server logs — most frameworks log the auth decision reason (expired, invalid signature, missing scope).
4. Reproduce in the browser devtools Network tab — inspect the request headers actually being sent (cookies especially).
5. If it worked yesterday, check what changed: secrets rotation, role migration, IP change, or a deploy that swapped middleware order.
When in doubt, the error message itself usually hints: many frameworks include reasons like 401 (invalid_token) or 403 (insufficient_scope) in the response body or WWW-Authenticate header — read those before touching any code.