HTTP 403 vs 401 Explained (With API Examples)
HTTP 401 vs 403: authentication vs authorization, when to return each, common causes in APIs and web servers, and how to fix them.

HTTP 403 vs 401 Explained (With API Examples)
Every developer has stared at a 401 or 403 wondering which one their code should actually return. The distinction matters because clients, browsers, proxies, and monitoring tools all behave differently based on the status code. This guide clears it up with real examples.
Table of Contents
- The one-sentence difference
- Authentication vs authorization
- When to return 401
- When to return 403
- Real API examples
- How browsers behave with each
- Server configuration examples (nginx, Apache, Express)
- Common misuse patterns
- Security best practices
- Troubleshooting
- FAQ

The One-Sentence Difference
401 = 'I don't know who you are.' 403 = 'I know who you are, and you can't have this.'
Authentication vs Authorization
Authentication is proving who you are — a password, an API token, a signed JWT. Authorization is what you're allowed to do — read this record, edit this user, delete this file.
A request that fails authentication returns 401. A request that authenticates but fails authorization returns 403.
When to Return 401
- No Authorization header at all on a protected endpoint.
- Malformed or invalid token (bad signature, wrong format).
- Expired session cookie or refresh token.
- Basic-auth credentials rejected.
Always include a WWW-Authenticate response header: WWW-Authenticate: Bearer realm="api" for JWT APIs, or Basic realm="admin" for HTTP Basic.
Inspect a live 401/403 response header in your browser with our HTTP Headers tool.
Check HTTP HeadersWhen to Return 403
- Authenticated user tries to access another user's data.
- User with 'viewer' role hits an admin-only endpoint.
- Read-only API key attempts a write operation.
- IP address blocked by WAF or rate limit.
- Feature flag disabled for this account.
Real API Examples
GET /api/users/me — no token
Correct: 401 Unauthorized, body { error: 'authentication_required' }, header WWW-Authenticate: Bearer.
GET /api/users/me — expired token
Correct: 401 Unauthorized. Client refreshes token, retries.
DELETE /api/users/999 — valid viewer token, admin-only route
Correct: 403 Forbidden, body { error: 'insufficient_permissions', required: 'admin' }.
GET /api/users/123 — valid token for user 456
Correct: 403 (or 404 if you don't want to leak that user 123 exists).
How Browsers Behave with Each
On 401 with WWW-Authenticate: Basic, browsers pop up a native username/password dialog. On 401 with Bearer, browsers do nothing special — your JavaScript handles it. On 403, no browser prompt fires — the response is treated like any other error.
Server Configuration Examples
nginx
location /admin { auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd; } — missing/invalid creds return 401. Blocked by IP (deny 1.2.3.4;) returns 403.
Apache
Same conventions via .htaccess: AuthType Basic + Require valid-user for 401 protection; Require ip 10.0.0.0/8 for 403-style IP restrictions.
Express (Node.js)
Return res.status(401).set('WWW-Authenticate', 'Bearer').json({...}) when no token, and res.status(403).json({error:'forbidden'}) when the token is valid but role is wrong.
Common Misuse Patterns
- Returning 403 for missing token — clients don't know to prompt login.
- Returning 401 for permission denials — clients uselessly try to refresh tokens.
- Returning 401 without WWW-Authenticate — violates RFC 9110 and confuses clients.
- Returning 200 with 'error' in the body — breaks monitoring and retries.
- Returning 403 to hide existence when 404 would be safer against enumeration attacks.
Security Best Practices
- Never leak whether a username exists in 401 responses — same status and message whether the account exists or not.
- Include no stack traces or internal details in 403 bodies.
- Log 401/403 with the offending IP for rate-limiting.
- Rotate API keys periodically to force clean 401s if a key leaks.
- Consider returning 404 instead of 403 for private resources to prevent enumeration.
Troubleshooting
- Getting 401 unexpectedly? Check that Authorization header is actually sent (proxies sometimes strip it).
- Getting 403 from your own code? Add logging around the permission check to see which rule fires.
- Getting 403 from Cloudflare? Check WAF events in the Cloudflare dashboard.
- Getting alternating 401 and 200? A token cache is inconsistent — clear and retry.
See every response header a URL returns.
Open HTTP Headers CheckerFrequently Asked Questions
Is 401 or 403 the right response for an expired token?+
401. Expired tokens are an authentication failure — the client should refresh and retry. 403 implies the token is valid but the user still can't access this resource.
Why does WordPress return 403 for /wp-admin?+
Because you're not logged in, WordPress considers the request unauthorized to see the admin page. Technically 401 would be more correct, but many CMS platforms return 403 by convention.
Should I include a WWW-Authenticate header on 401?+
Yes. RFC 9110 requires it. It signals the auth scheme (Bearer, Basic, Digest) the client should use.
What's the difference between 403 and 404?+
403 says 'this exists but you can't have it'. 404 says 'this does not exist'. Some sites return 404 instead of 403 to hide the existence of private resources.
Can Cloudflare or a WAF return 403?+
Yes. Cloudflare, AWS WAF, and similar services return 403 for blocked requests (rate limits, bot detection, geo blocking). Check the response body — it usually says 'blocked by security rules'.
Why does my nginx return 403 for a static file?+
Usually filesystem permissions — nginx can't read the file — or a missing index.html when directory listing is off. Check the nginx error log.
Does 401 or 403 count as a client error or server error?+
Both are 4xx — client errors. They mean the client did something wrong (didn't authenticate, or authenticated but requested something not allowed).
Should APIs return 200 with an error body instead of 401/403?+
No. Use the correct status code so browsers, proxies, and monitoring tools handle it correctly. Include structured error details in the body.
Related articles
Browse all in Developer ToolsHow to Check If a Website Is Down
Read Developer ToolsWhat Is WHOIS Lookup and How to Use It
Read Developer ToolsWhat Is HTTP Status Code (200, 404, 500 Explained)
Read Developer ToolsBest Free Online Tools for Developers (2026)
Read Developer ToolsWhat Is Web Scraping? How It Works, Uses, and Tools (2026)
Read Developer ToolsWhat Is a CDN (Content Delivery Network)? Guide 2026
ReadTry the related free tools
Hands-on utilities from DigiMetrics Hub that go with this guide.
JWT Decoder
Decode and inspect any JWT token instantly. Free online JWT decoder with header and payload display.
Open tool Developer ToolsHTTP Headers Checker
Inspect HTTP response headers for any URL. Free online header checker for developers.
Open tool Developer ToolsJSON Formatter
Format, validate and beautify JSON data instantly. Free online JSON formatter, no signup needed.
Open tool Developer ToolsBase64 Encoder
Encode or decode Base64 strings instantly online. Free Base64 converter, no signup required.
Open tool