4xx Client error · RFC 9110 §15.5.4

403 Forbidden

The server understood the request and refuses to authorise it.

What 403 means

Authorisation, not authentication: the identity is known (or irrelevant) and access is denied. Re-sending credentials will not help.

Web servers return 403 for directory listings that are disabled, files without read permission, IP allowlists, WAF rules and CSRF failures. Cloud storage (S3) returns 403 rather than 404 for missing objects when the caller lacks list permission.

Common causes

  • Insufficient role or permission for the resource.
  • File-system permissions or SELinux denying the web server.
  • WAF, geo-block or IP allowlist.
  • CSRF token missing or invalid on a form post.
  • S3 or similar hiding a missing object from an unprivileged caller.

How to fix it

  • Check the user's role and the resource ACL.
  • On the server: ls -l the file, check the process user, check audit logs for SELinux denials.
  • Look for a WAF or CDN rule id in the response body or headers.

What it looks like

A typical response:

HTTP/1.1 403 Forbidden
Content-Type: application/json

{"error":"forbidden","detail":"role viewer cannot delete projects"}

The same event in an nginx access log (the status is the number after the request line):

203.0.113.7 - - [10/Sep/2026:10:12:01 +0000] "GET /admin HTTP/1.1" 403 153 "-" "Mozilla/5.0"

Check it with curl

-i prints the status line and headers, and -w '%{http_code}' prints only the number, which is handy in scripts and health checks. Replace the URL with yours:

curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://example.com/api/orders

Compare what curl sees with what the browser sees. A different status from the same URL usually means a cache, a CDN edge or a cookie is in the way.

Investigating a run of 403s? Paste the log excerpt into Log Share to get line numbers, highlighting and an expiring link for whoever is on call with you.

  • 401Unauthorized: Authentication is required or the credentials sent are invalid.
  • 404Not Found: The server found no resource at that URL.
  • 429Too Many Requests: The client has sent too many requests in a given amount of time.

FAQ

Why does S3 return 403 for a file that does not exist?

Without s3:ListBucket permission, S3 hides whether the key exists and returns 403 instead of 404 to avoid leaking names.

Should I return 404 instead of 403 to hide resources?

It is a legitimate choice when the existence of the resource is itself sensitive, and RFC 9110 explicitly allows it.