HTTP 403 Forbidden – Free, Online, Private
403 Forbidden means the server understood the request and refuses to authorize it.
What HTTP 403 means
403 means the client is not allowed to do this, even if you explained it again. Authentication may have succeeded. The refusal can also hide whether the resource exists, which is why some APIs return 404 instead of 403 for objects the caller must not know about. The response should not include a next step that leaks a secret.
403 Forbidden belongs to the 4xx client error class. Clients that only look at the class will group it with its neighbors. Clients that care about the exact code should use the narrower rule on this page, because the class is only the first digit.
When you see HTTP 403
When you see HTTP 403 Forbidden, stop on that exact status line. 403 Forbidden means the server understood the request and refuses to authorize it. The trace that usually produces it: An authenticated user touched a resource owned by someone else. The 4xx client error class only tells you the first digit. A neighbor in that class is a different fault, and the fix on this page does not transfer to it.
Return 403 when the identity is known, or when you are refusing regardless of identity, and the request is well formed. Use 401 if credentials are missing or bad. Use 404 if revealing that the resource exists would itself be a leak. Do not return 403 for a validation error.
How to fix HTTP 403
To fix HTTP 403, change the condition that made the server choose Forbidden. Do not retry the same request. Retrying will not grow the caller's permissions. If the user can request access through a separate flow, send them there. Otherwise stop. An unchanged retry fails again. Retry only after an authorization change. If the log line is a different code, leave this page. The mix-up people make with 403 is this one: 401 asks for credentials. 404 hides existence. 400 says the request was malformed. 403 says the request was understood and refused.
- A CSRF check failed and the server refused the cookie-authenticated browser call.
- The server returned 403 for a missing file because a rule said to hide 404s, and the client kept asking the user to request access.
- An IP allowlist rejected the caller before the application ran.
An access log that only stores 403 is not enough to tell these apart. Keep the method, the path, and a request id. Do not keep the Authorization header or a request body that might be a token. The status explains the outcome. It is not a place to echo secrets back to the client either.
Example response
HTTP 403 ForbiddenHTTP/1.1 403 Forbidden
Content-Type: application/json
{"error":"not allowed"}
The body can be short. If you tell the caller exactly which permission is missing, you may be telling them about a permission they should not know exists. Pick the level of detail on purpose. Do not include a stack trace.
What the client should do
Do not retry the same request. Retrying will not grow the caller's permissions. If the user can request access through a separate flow, send them there. Otherwise stop.
Do not let a shared cache store a 403 as the resource for every user. A private 403 for one account must not be reused for another. Cache-Control: private is the safe default.
An unchanged retry fails again. Retry only after an authorization change.
Codes people confuse with HTTP 403
401 asks for credentials. 404 hides existence. 400 says the request was malformed. 403 says the request was understood and refused.
Frequently asked questions
Does 403 mean the password was wrong?
No. A wrong password is 401. 403 means the server will not authorize the action.
Why do some APIs return 404 instead?
To avoid confirming that the resource exists. That is a product choice, not a requirement of 403.
Should the client retry?
Not with the same credentials and the same request.
Can 403 include a WWW-Authenticate header?
It can, but if the real problem is missing credentials, send 401.
What should I do first when I see HTTP 403?
Read the status as 403 Forbidden specifically. 403 Forbidden means the server understood the request and refuses to authorize it. Then check the cause that matches the request you sent, starting with: An authenticated user touched a resource owned by someone else.