Skip to main content

Nginx 403 Forbidden: common causes and how to fix it

Nginx · 13.10.2026 · 5 min read
Illustration for “Nginx 403 Forbidden: common causes and how to fix it”

A 403 Forbidden response from nginx means the server understood the request but refused to carry it out — unlike 404, the file or route is found, but access to it is closed. The breakdown below is aimed at an administrator who sees 403 in the browser or in the logs and wants to find the cause quickly, instead of guessing directives at random.

What the 403 code means in nginx logs

In access.log, a 403 entry appears next to all other requests, but nginx does not always explain the cause in that line — for details, check error.log, which usually names the exact file or directive behind the refusal.

403 differs from 401: with 401 the browser shows a login prompt, with 403 it is simply a refusal without any authorization request. These are different mechanisms, and confusing them during diagnosis does not help.

The most common cause — index not found and autoindex off

If a location has no file named by the index directive — usually index.html or index.php — and autoindex is off (the default), nginx returns 403 instead of a directory listing. That is protective behavior, not a configuration mistake.

Checking it is simple: look for the needed file at the root of the location and check the index value in the config. Basic setup is covered in the article on configuring nginx.

File and directory permissions

The nginx process runs as a dedicated user — most often www-data or nginx — and that user needs read access to the file and execute access (not read!) to every directory along the path to it. That is a common cause of 403: the file itself is readable, but one of the parent directories is closed to outsiders.

ItemNeeded permissionCommon mistake
File at the site rootread for the process ownermode 600 for another user only
Site directoryexecute for traversalmode 700 with no group or other
User home directoryexecute for group or othermode 750 blocks nginx access
Uploaded filesread after a script uploads themfile created with mode 600
PHP-FPM socketaccess for the nginx groupsocket owned only by php-fpm

Allow and deny directives and IP restrictions

The allow and deny directives restrict access by client address and often stay in the config after a temporary block — for example, an administrator closed access during maintenance and forgot to lift the restriction.

Rules are read top to bottom, and an implicit deny all at the end of a block is a common reason a given IP does not match any allow rule. Similar restrictions are covered in the article on hardening nginx, which explains how not to lock yourself out.

Configuration conflicts between several location blocks

When two location blocks match the same path, nginx picks by priority rules — exact match first, then the longest prefix match, then a regular expression. If a broader block happens to contain a deny directive, it overrides the more specific block that was meant to apply.

Diagnosing such conflicts is easiest by reading the full expanded configuration rather than a single file: included directives turn a simple-looking setup into several levels of nesting.

How to find the cause and fix it

The diagnostic order that covers most cases:

  1. look at the exact line in error.log — it names the file or directive behind the refusal;
  2. check permissions on the file and on every directory along the path with namei -l;
  3. check against the expanded config with nginx -T to see all includes at once;
  4. temporarily remove the allow and deny directives and check whether the error disappears;
  5. reload the configuration with nginx -s reload after the fix and check the request again.
sudo tail -n 20 /var/log/nginx/error.log
sudo namei -l /var/www/site/public/index.html
sudo nginx -t
sudo nginx -s reload

After fixing the cause, it is worth checking whether buffer and cache settings cause similar errors under growing load — that is covered in the article on optimizing nginx on a VPS.

Frequently asked questions

How does 403 differ from 404 in an nginx response?

404 means the server did not find the resource at the given path at all. 403 means the opposite: the resource exists, but the server deliberately refuses access — because of file permissions, allow and deny directives, or a missing index file with autoindex off.

Why does 403 happen if the file itself is readable but a directory above it is closed?

Traversing a directory needs execute permission, not read permission. If one of the parent directories along the path lacks that permission for the user nginx runs as, the server cannot reach the file even if the permissions on the file itself are correct.

Does autoindex need to be enabled to get rid of 403?

Not necessarily, and usually it should not be: autoindex opens the directory file listing to every visitor, which is itself a risk for private data. It is better to add the missing index file or point the location at the right path explicitly.

Can 403 appear because of SELinux rather than nginx itself?

Yes, on systems with SELinux enabled, a file's context is checked separately from regular Linux permissions: even correct read permissions will not help if the context label does not allow access to the nginx process. In that case the refusal shows up in the SELinux audit log, not only in the nginx error.log.

Was this article helpful?
← Back to Knowledge Base Ask Support