Mark van Eijk · Rocketeers

Knowledge

#Errors

A 403 Forbidden means nginx found the resource but refuses to serve it. Almost always a file permission problem, a missing index file, or an explicit deny rule.

Published by Mark van Eijk on June 23, 2026 · 1 minute read

  1. About error 403
  2. Why do I see this error
  3. Solution
  4. Fix permissions
  5. Missing index file
  6. Check for deny rules

About error 403

A 403 Forbidden is returned when nginx successfully locates the request but is not allowed to serve it. Unlike a 404, the resource exists, nginx just won't hand it over.

Why do I see this error

The usual causes, in rough order of likelihood:

  • File or directory permissions the nginx worker user can't read or traverse.
  • The URL points at a directory with no index file and autoindex off.
  • An explicit deny rule in the configuration.
  • Wrong ownership on the document root after a deploy.

As always, the nginx error log names the exact reason:

tail -f /var/log/nginx/error.log

You'll see lines like directory index of "/var/www/html/" is forbidden or Permission denied.

Solution

Fix permissions

nginx must be able to read files and traverse (execute bit) every directory in the path. Directories should be 755 and files 644:

sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;

Ownership should match the web server user (www-data on Debian/Ubuntu, nginx on RHEL):

sudo chown -R www-data:www-data /var/www/html

For a Laravel app the web root is the public directory, and only storage and bootstrap/cache need to be writable, see Laravel failed to open stream: Permission denied.

Missing index file

If the request is for a directory, make sure an index file is defined and present:

index index.php index.html;

Avoid turning on autoindex on; on a public server, it exposes your file listing.

Check for deny rules

Search your config for an explicit block you may have forgotten:

location ~ /\.(?!well-known) {
    deny all;
}

That example (blocking dotfiles) is correct, but an over-broad deny is a common self-inflicted 403. Reload after any change:

nginx -t && systemctl reload nginx

If the document root or PHP socket itself is misconfigured rather than just permissions, start from a clean Nginx install. A bad config can just as easily produce a 502 Bad Gateway instead.

Read the original on rocketeersapp.com ↗