Troubleshoot Nginx 403 responses across a site
Use the error log to separate filesystem permissions, SELinux, path mapping, and access rules before changing ownership.
When every file returns 403, start with one failing request and its error-log entry. The status alone cannot tell you whether Nginx, PHP, or another upstream denied access.
sudo tail -50 /var/log/nginx/error.log
Permission denied suggests filesystem or security-policy checks. directory index ... is forbidden points to directory handling. access forbidden by rule points to Nginx access rules.
Check the worker's access to the whole path
ps aux | grep '[n]ginx: worker'
namei -l /var/www/html/index.html
The worker needs search permission on each parent directory and read permission on the target file. A site beneath a private home directory may be blocked before Nginx reaches the otherwise readable file.
For an intentionally public, static-only document tree, conventional permissions are:
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
Apply this only after confirming that the tree contains public content and no files requiring more restrictive modes. Keep deployment ownership with the deployment account; Nginx generally does not need ownership or write permission. A dedicated read-only group or ACL can provide narrower access than world-readable modes.
Check SELinux without disabling it
getenforce
ls -Z /var/www/html/
sudo ausearch -m AVC -ts recent
For a nonstandard public-content directory, a persistent context mapping can look like:
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/site(/.*)?'
sudo restorecon -Rv /srv/site
Use -m when modifying an existing mapping. restorecon applies the policy's expected labels; it does not invent a mapping for an arbitrary location. Diagnose audit denials rather than turning enforcement off as a routine fix.
Verify root, alias, and access rules
Inspect the complete configuration with nginx -T, then compare the logged filesystem path with the deployed files and symlink targets. A nonexistent file or directory normally produces a 404 in straightforward static serving; a wrong root is not guaranteed to produce a 403.
Also inspect inherited access rules:
location / {
allow 192.168.1.0/24;
deny all;
}
This deliberately rejects clients outside the allowed network. Behind a proxy, verify the trusted-client-IP setup before changing allowlists.
Retest a page, a static asset, and an intentionally restricted path. A fix should restore allowed requests without making restricted content public. Guance HTTP synthetic tests can check expected endpoint responses; collected access logs can show which URIs still return 403.
FAQ
Will chmod 777 fix it?
It may remove a Unix permission barrier while unnecessarily granting write access. It cannot fix a wrong path, an Nginx deny rule, or SELinux policy. Correct the actual access requirement instead.
What if only PHP requests fail?
Inspect the matched PHP location, FPM logs, script permissions, and FPM restrictions such as security.limit_extensions. Static-file success does not rule out permissions on different PHP files or directories. Socket connection failures commonly produce 502, so correlate the actual logs.
Does the same approach apply in a container?
Yes, but inspect numeric UIDs/GIDs, mount options, parent paths, and security labels from inside the container. Do not change the container's runtime user blindly; grant the intended worker the required access to the mounted content.
References: Nginx request processing, SELinux configuration for nonstandard paths.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan