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
- About error 403
- Why do I see this error
- Solution
- Fix permissions
- Missing index file
- 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
autoindexoff. - An explicit
denyrule 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.