VPS Tutorials
VPS Website Returns 403 Forbidden (2026): Nginx/Apache Permissions, index, and SELinux Troubleshooting Checklist
When a VPS website connects but returns 403 Forbidden, it's usually not a network failure — the web server is refusing access. This article walks through Nginx, Apache, directory permissions, index files, SELinux, reverse proxies, and common WordPress scenarios step by step.

I've seen plenty of people who, the moment they see a 403, rush to change DNS, restart the server, or even suspect the VPS is blocked. In fact, 403 Forbidden is not the same thing as "can't connect."
If the browser can open the page but shows 403 Forbidden, that means the request has already reached your VPS, and Nginx or Apache received it — it just decided that this path is not allowed to be accessed.
The most common causes fall into a few categories: the directory has no index file, file permissions are wrong, the site directory owner is wrong, the Nginx/Apache config contains a deny, SELinux is blocking, or WordPress or panel rules are blocking the request.
Below is the actual troubleshooting order — don't start by blindly changing permissions.
Start by checking the response code with curl:
curl -I https://example.com
If you see something like:
HTTP/1.1 403 Forbidden
Server: nginx
that means the web service has responded — it's just refusing access.
If instead you see:
404 Not Found: the path or file does not exist;502 Bad Gateway: something behind Nginx such as PHP-FPM, Node, or Gunicorn is failing;521/522: Cloudflare failed to connect to the origin or timed out;403 Forbidden: focus on permissions, directories, rules, and security policies.
This distinction matters. For a 403, you don't check the network first — you check why the web server won't allow access.
Nginx:
sudo tail -n 80 /var/log/nginx/error.log
Apache:
sudo tail -n 80 /var/log/apache2/error.log
On CentOS / AlmaLinux / Rocky Linux, Apache may be:
sudo tail -n 80 /var/log/httpd/error_log
Common log entries look roughly like this:
directory index of "/var/www/site/" is forbidden
Or:
open() "/var/www/site/index.html" failed (13: Permission denied)
These two mean completely different things.
The first is that the directory has no index file and the server doesn't allow directory listing; the second is that the file exists but the web server user doesn't have permission to read it.
Many 403s happen because you accessed a directory that has no default homepage.
Look at the site directory:
ls -lah /var/www/site
There should be at least one of:
index.html
index.htm
index.php
The Nginx config must also be correct:
index index.html index.htm index.php;
For Apache, check DirectoryIndex:
DirectoryIndex index.html index.php
If the directory really has no index file, Nginx returning 403 is normal. It's not that it can't open — it's that it won't show the entire directory listing to others.
For a temporary test, drop in a file:
echo 'ok' | sudo tee /var/www/site/index.html
Then visit the site again. If the 403 disappears, the problem is the index file or the site root configuration.
Many people only look at index.html permissions and overlook parent directories.
To read a file, the web server doesn't just need to read the file itself — it also needs to be able to enter the parent directories. For example, with /var/www/site/index.html, each level — /var, /var/www, /var/www/site — needs execute permission.
This command is the most intuitive:
namei -om /var/www/site/index.html
Normally it looks roughly like:
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-xr-x www-data www-data site
-rw-r--r-- www-data www-data index.html
Common recommendations:
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;
Don't just blast everything with chmod -R 777. It may indeed make the 403 disappear, but it also wipes out your security.
On Ubuntu / Debian, the common users for Nginx and Apache are www-data. You can set the owner like this:
sudo chown -R www-data:www-data /var/www/site
On CentOS / AlmaLinux / Rocky Linux, the common Apache user is apache, and Nginx may be nginx:
sudo chown -R nginx:nginx /var/www/site
If you're not sure who the user is, check the processes:
ps aux | grep -E 'nginx|apache|httpd'
Nginx 403s are often not Linux permissions but a config that explicitly disallows access.
First test the config:
sudo nginx -t
Then look at the site config:
sudo nginx -T | grep -nE 'server_name|root|location|deny|allow|try_files|autoindex'
Focus on these:
location / {
try_files $uri $uri/ =404;
}
If a request for / matches a directory that has no index file, you may get directory index is forbidden.
Also look for rules like:
deny all;
Or a location that blocks a directory:
location /uploads/ {
deny all;
}
If the 403 only happens on a certain path, such as /admin/, /uploads/, or /.well-known/, focus on the corresponding location.
After changing the config:
sudo nginx -t
sudo systemctl reload nginx
Apache 403s often show up in these places.
The site directory must allow access:
<Directory /var/www/site>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
If you see:
Require all denied
that's pretty clear — it's denying access.
There's also .htaccess. After migrating WordPress, Laravel, or an old project, .htaccess can easily scramble path rules.
You can temporarily rename it to test:
sudo mv /var/www/site/.htaccess /var/www/site/.htaccess.bak
sudo systemctl reload apache2
If the 403 disappears, the problem is in .htaccess. Don't rush to delete it — add the rules back gradually.
Apache config test:
sudo apachectl configtest
Reload on Ubuntu / Debian:
sudo systemctl reload apache2
CentOS family:
sudo systemctl reload httpd
If you're on Rocky Linux, AlmaLinux, or CentOS Stream, permissions look correct, but you still get a 403, it's very likely SELinux.
First check the status:
getenforce
If it returns:
Enforcing
then check the context:
ls -Z /var/www/site
Web content usually needs a context like httpd_sys_content_t.
Common fix:
sudo restorecon -Rv /var/www/site
If your site directory is not in a standard location — for example, under /home/deploy/site — you need to set the context separately. Don't just turn off SELinux as a permanent solution. For temporary verification you can use:
sudo setenforce 0
If the 403 disappears immediately after turning it off, you're on the right track. Remember to restore it after verifying:
sudo setenforce 1
Then handle it with the correct context or boolean.
If you use WordPress, a 403 can also come from plugins, firewall rules, or permalinks.
Try this order:
- Temporarily rename the security plugin directory;
- Regenerate permalinks;
- Check
.htaccessor the Nginx rewrite rules; - Check
wp-content/uploadspermissions; - Look at the web server error log, not just the browser page.
BT Panel users should also check:
- Whether the site directory is set wrong;
- Whether hotlink protection is blocking by mistake;
- Whether URL rewrite rules match incorrectly;
- Whether a WAF or security plugin is blocking;
- Whether the site root has
index.htmlorindex.php.
Panels lower the barrier to operations, but the root causes of 403 are still the same categories: directories, permissions, rules, and security policies.
| Symptom | Most likely cause | What to check first |
|---|---|---|
| Homepage 403, log says directory index is forbidden | No index file | ls -lah, Nginx/Apache index config |
| Log says Permission denied | Insufficient file or parent directory permissions | namei -om, owner, chmod |
| Only one directory returns 403 | location / .htaccess blocking | Nginx location, Apache Directory, .htaccess |
| CentOS permissions look fine but still 403 | Wrong SELinux context | getenforce, ls -Z, restorecon |
| WordPress admin or upload directory 403 | Plugin/WAF/rewrite/directory permissions | Plugins, .htaccess, uploads permissions |
| 403 shown behind Cloudflare | Origin or Cloudflare rule blocking | Origin logs, WAF, firewall rules |
When I actually hit a 403, I generally don't reinstall the environment first, and I don't immediately change to 777.
I go in this order:
curl -I https://example.com
sudo tail -n 80 /var/log/nginx/error.log
ls -lah /var/www/site
namei -om /var/www/site/index.html
sudo nginx -T | grep -nE 'root|index|location|deny|try_files'
If it's Apache, I swap in:
sudo tail -n 80 /var/log/apache2/error.log
sudo apachectl configtest
grep -R "Require all" /etc/apache2/sites-enabled /etc/apache2/apache2.conf
For the CentOS family, add:
getenforce
ls -Z /var/www/site
Following this order, you can usually pinpoint which layer the problem is on within about ten minutes.
403 Forbidden is most easily misjudged as "the site is down." In fact, it's often good news: the request has reached the VPS, the web service is alive, and it's just some rule or permission that won't let it read the file.
Don't reinstall Nginx, restart the VPS, or change DNS right away. First read the error log, then check index, permissions, config, and SELinux. As long as the log shows directory index is forbidden or Permission denied, the direction is clear.