Skip to main content
How To Fix a 403 Forbidden Error on Your VPS - Virtarix Blog

How To Fix a 403 Forbidden Error on Your VPS

October 7, 2025 · Blog / Technical Guides

You are trying to access your website and boom, 403 Forbidden Error. The server understood your request perfectly but decided to slam the door in your face anyway. This is not a "page not found" situation. The page exists. The server just will not let you see it. And if you are running a VPS, chances are you caused this yourself. Don’t worry, it happens to everyone. At Virtarix, we often see users run into 403 errors. With over a decade of experience in cloud hosting, we understand their root causes and how to fix them quickly.

Here is the thing about VPS hosting. You have full control. That is powerful. But it also means you can accidentally lock yourself out of your own site by changing the wrong permission, messing up an .htaccess rule, or letting your firewall get a little too aggressive.

Understanding the 403 forbidden error

A 403 error means the server understood your request but refused to process it due to insufficient permissions or application logic restrictions. Think of it like showing up to a private party. The bouncer knows you are there, but you are not on the list.

The server is not broken. The file exists. You just do not have permission to access it.

This is different from a 404 error where the page does not exist at all, or a 401 error where you need to log in with credentials. With a 403, even if you had a username and password, it would not help. The problem is deeper. It is about server-level permissions.

On a VPS, you will see 403 errors more often than on shared hosting because you are managing everything yourself. On shared hosting, the host sets safe defaults. On a VPS, if you set file permissions to something unusual at 2 AM while troubleshooting another issue, you may find out the next morning that you have caused the error.

Step-by-step guide to fix a 403 forbidden error

Start simple. Don't immediately SSH into your server and start changing permissions. Rule out the easy stuff first.

Step 1: check the simple stuff first

Refresh the page because many 403 errors are temporary. Press Ctrl+Shift+R (or Cmd+Shift+R on Mac) to do a hard refresh that bypasses your browser cache.

Check the URL carefully. If you are trying to access a directory without a trailing slash or with a typo, you might hit a 403. For example, example.com/admin might work while example.com/admin/ returns 403 if there is no index file in that folder.

Clear your browser cache and cookies. Old cached permission states can cause phantom 403 errors. Better yet, test in an incognito or private window. It starts clean every time.

If you are using a VPN or proxy, turn it off temporarily. VPN IP addresses often get abused and end up on blocklists. Try accessing your site from your phone on cellular data, not WiFi. If it works there but not on your computer, the problem is on your end. It could be cached data, a browser extension, or your IP is blocked.

Browser-level checks

Open your browser developer tools (F12), go to the Network tab, and reload the page. Look at the response headers for the failed request. This shows you whether the 403 is coming from your server, a CDN, or something in between.

Network-level checks

You can also use curl to quickly diagnose where the 403 is coming from:

“`bash title="Inspect the site's HTTP response headers" curl -I https://yoursite.com


Look for these headers in the output:

- Server: tells you if it is Apache, Nginx, or a CDN responding
- X-Powered-By: might reveal if PHP is involved
- CF-Ray: indicates Cloudflare is in the path

Status code 403 with Server: cloudflare means the CDN is blocking you, not your VPS.

If you see your actual server name (like Apache/2.4.41 or nginx/1.18.0), the block is coming from your VPS, not external services.

### Step 2: fix file permissions via SSH

This step is where you will fix 80% of permission-based 403 errors. You need SSH access to your VPS for this.

Connect to your server and navigate to your web root (usually /var/www/html or /home/username/public_html).

Check current permissions with:

```bash title="List files with permissions and ownership"
ls -la

You will see output like -rw-r–r– for files and drwxr-xr-x for directories. If you see anything like -rw——- or drwx——, that is the problem.

Fix directory permissions:

“`bash title="Attempt to set directory permissions recursively" find /var/www/html -type d -exec chmod 755 {} ;


Fix file permissions:

```bash title="Attempt to set file permissions recursively"
find /var/www/html -type f -exec chmod 644 {} ;

These commands recursively set correct permissions. 755 on directories means the owner can read, write, and execute. Everyone else can read and execute, which means they can navigate through. 644 on files means the owner can read and write. Everyone else can only read.

CRITICAL WARNING: Never use 777 permissions. Yes, it fixes permission errors immediately. It also lets anyone execute any code on your server. Servers can be compromised within hours of setting 777 permissions because bots scan for them.

All Virtarix VPS plans grant full root access, giving you complete flexibility to correct permissions without delay.

If you are running WordPress or another CMS, some files need special permissions. For example, WordPress needs wp-config.php set to 600 or 640 for security. Always check your CMS documentation.

Step 3: fix file ownership

Even with correct permissions, wrong ownership breaks everything. Your web server needs to be able to read files.

Check current ownership:

“`bash title="Check current file ownership" ls -la


Look for the username and group in the third and fourth columns. If they are both root, that is likely the issue.

Find out what user your web server runs as:

```bash title="Find the web server process user"
ps aux | grep -E 'apache|nginx|httpd'

Usually it is www-data, nginx, apache, or nobody. Let’s say it is www-data.

Fix ownership recursively:

Safety check: Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.

“`bash title="Assign the web root to www-data" chown -R www-data:www-data /var/www/html


This changes both the user and group ownership. Now your web server can access the files even if permissions are restrictive.

For WordPress, you typically want files owned by your user account so you can edit them, but readable by www-data so the web server can serve them.

In that case:

> **Safety check:** Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.

```bash title="Assign the web root to a user and www-data group"
chown -R yourusername:www-data /var/www/html

Then make sure group permissions are set correctly. Look for the r in the group permission section.

Step 4: check your .htaccess file

If you are running Apache, .htaccess files control access. A single bad rule can lock everyone out.

First, test if .htaccess is the problem.

Rename it:

“`bash title="Temporarily rename the .htaccess file" mv .htaccess .htaccess.backup


Refresh your site. If it works now, .htaccess was the culprit.

Most common .htaccess mistakes include:

- Deny from all without a corresponding Allow statement
- Requiring authentication that is not configured
- Rewrite rules that create infinite loops
- IP whitelisting that does not include your current IP

Apache 2.4 Syntax Change: If you are running Apache 2.4 or newer, the old access control syntax does not work anymore. Lines like:

```apache title="Show legacy Apache access-control directives"
Order allow,deny
Allow from all

will cause 403 errors. You need to use the new syntax:

“`apache title="Grant Apache access to every client" Require all granted


Or to allow specific IPs:

```apache title="Grant Apache access to one IP address"
Require ip 192.168.1.100

If you are using WordPress, you can regenerate a default .htaccess by going to

Settings → Permalinks and clicking Save. WordPress writes a fresh .htaccess file.

For a basic WordPress .htaccess:

“`apache title="Configure basic WordPress permalink rewrites"

BEGIN WordPress

RewriteEngine On RewriteBase / RewriteRule ^index.php$ – [L] RewriteCond %{REQUESTFILENAME} !-f RewriteCond %{REQUESTFILENAME} !-d RewriteRule . /index.php [L]

END WordPress


Restore your old .htaccess one section at a time to find the problematic rule.

### Step 5: verify index files exist

If you are getting a 403 on a directory, check if an index file exists.

```bash title="List the blog directory contents"
ls -la /var/www/html/blog/

Look for index.html, index.php, or whatever your default document is set to in your web server config.

If it is missing, you have three options:

  • Add an index file
  • Enable directory listing (not recommended for security)
  • Set up a redirect

For Apache, your default document order is usually defined in the main config or virtual host file. It typically prioritizes index.html, then index.php, then others.

If you see an index file but still get 403 errors, the file itself might have wrong permissions:

“`bash title="Set the blog index file to mode 644" chmod 644 /var/www/html/blog/index.php


### Step 6: verify virtual host configuration

If you host multiple sites on your VPS, the wrong virtual host config might be served for your domain.

For Apache, check which virtual hosts are enabled:

```bash title="Show enabled Apache virtual hosts on Debian"
apache2ctl -S

or on CentOS/RHEL:

“`bash title="Show enabled Apache virtual hosts on RHEL" httpd -S


This shows all configured virtual hosts and which one matches your domain. Look for your domain in the output and verify it points to the correct document root.

For Nginx, check your site configuration:

```bash title="List Nginx server_name directives"
nginx -T | grep server_name

If your domain is not listed or points to the wrong location, edit your virtual host file.

  • For Apache, it is usually in /etc/apache2/sites-available/ or /etc/httpd/conf.d/.
  • For Nginx, check /etc/nginx/sites-available/ or /etc/nginx/conf.d/.

After making changes, test the configuration:

“`bash title="Test the Apache configuration" apache2ctl configtest


or for Nginx:

```bash title="Test the Nginx configuration"
nginx -t

Then restart the web server:

“`bash title="Restart Apache" systemctl restart apache2


or

```bash title="Restart Nginx"
systemctl restart nginx

Step 7: fix symlink issues

If error logs mention symlinks or you are using symbolic links, your server may have symlink following disabled.

For Apache, add this to your .htaccess file or virtual host configuration:

“`apache title="Allow Apache to follow symbolic links" Options +FollowSymLinks


For Nginx, symlinks are followed by default, but check for any disable_symlinks directives that may be blocking them.

After making changes, restart the service:

```bash title="Restart Apache after a symlink change"
systemctl restart apache2

Or for Nginx:

“`bash title="Restart Nginx after a symlink change" systemctl restart nginx


### Step 8: check for IP blocks

Another common cause of 403 errors is when your server blocks your IP address using tools like fail2ban, firewalls, or security plugins.

Check fail2ban first (if installed):

```bash title="List active Fail2Ban jails"
fail2ban-client status

This shows active jails. Then check if your IP is banned:

“`bash title="Inspect one Fail2Ban jail" fail2ban-client status [jail-name]


If your IP is in the banned list, unban it:

```bash title="Unban an IP address from a Fail2Ban jail"
fail2ban-client set [jail-name] unbanip YOUR.IP.ADDRESS

Important: If your IP keeps getting banned, it will only happen again unless you fix the root cause (like repeated failed logins). Alternatively, whitelist your IP permanently.

Also, check firewall rules:

“`bash title="List iptables rules numerically" iptables -L -n


Or with UFW:

```bash title="Show UFW firewall status"
ufw status

If you use a CMS like WordPress with security plugins (Wordfence, iThemes Security), log into the admin panel and review blocked IPs.

Sometimes, shared IPs on VPNs or mobile networks get flagged. In this case, whitelisting your IP or IP range is the best fix.

Step 9: check mod_security rules

If your VPS has mod_security installed (very common on cPanel or hardened servers), it may block legitimate requests.

To check if it is active:

“`bash title="Check whether Apache loaded a security module" apachectl -M | grep security


Or check your Apache error logs:

```bash title="Search the Apache error log for ModSecurity"
grep -i "ModSecurity" /var/log/apache2/error.log

You’ll see entries with rule IDs that triggered the block. These often cause false positives for form submissions, file uploads, or certain patterns.

To temporarily disable mod_security for testing, add this to .htaccess:

“`apache title="Disable ModSecurity in .htaccess for testing"

SecRuleEngine Off


If your site works afterward, you will need to whitelist specific rules or adjust the configuration.

For cPanel users, go to:

```text title="Open ModSecurity Tools in WHM"
 WHM → Security Center → ModSecurity Tools 

to manage rules.

Step 10: check PHP-FPM permissions (Nginx users)

If you are running Nginx with PHP-FPM, permission issues often happen when PHP-FPM runs under a different user than the file owner.

Check what user PHP-FPM runs as:

“`bash title="Find the PHP-FPM process user" ps aux | grep php-fpm


It is usually www-data or nginx. This user must have read access to your PHP files.

Next, check your pool configuration:

```bash title="Inspect the PHP-FPM pool user and group"
cat /etc/php/7.4/fpm/pool.d/www.conf | grep -E 'user|group'

(Adjust the PHP version as needed). Make sure the user and group match file ownership or that group permissions allow access.

If you need to change the PHP-FPM user, edit the pool configuration and restart:

“`bash title="Restart PHP 7.4 FPM" systemctl restart php7.4-fpm


### Step 11: troubleshoot CDN issues

If you are using a CDN (Cloudflare, StackPath, etc.), it might be blocking requests or caching old permissions.

To test, temporarily disconnect your CDN. In Cloudflare, set DNS to DNS only (gray cloud) instead of Proxied (orange cloud).

If the site works without the CDN, the issue lies in the CDN configuration. Try purging the cache:

- Cloudflare: Go to *Caching → Configuration → Purge Everything*.

Also, review CDN firewall rules. In Cloudflare, the Firewall tab shows blocked requests and allows you to whitelist your IP.

Some CDNs display challenge pages that appear as 403 errors. Lowering security settings temporarily can confirm this.

### Step 12: verify DNS points to the right server

Sometimes DNS points to an old VPS you migrated from.

Check your domain’s A record:

```bash title="Query a domain's IPv4 address record with dig"
dig yourdomain.com A

or

“`bash title="Resolve a domain with nslookup" nslookup yourdomain.com


Compare the result to your current VPS IP. If it’s wrong, update your A record at your DNS provider.

Modern DNS usually updates in minutes to hours, though high TTL values can delay propagation up to 24 hours.

Flush your local DNS cache to see changes faster:

- Windows:

```powershell title="Flush the Windows DNS resolver cache"
ipconfig /flushdns
  • Mac:

“`bash title="Flush the macOS DNS resolver cache" sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder


- Linux:

```bash title="Flush the systemd DNS resolver cache"
sudo systemd-resolve --flush-caches

Step 13: check server error logs

When all else fails, check the error logs. They usually tell you exactly what’s wrong.

For Apache:

“`bash title="Follow the Apache error log" tail -f /var/log/apache2/error.log


For Nginx:

```bash title="Follow the Nginx error log"
tail -f /var/log/nginx/error.log

On Virtarix, you always have SSH access to logs. If you need help interpreting them, our support team is available around the clock.

Reproduce the 403 error while watching the logs. You’ll see messages like Permission denied, Directory index forbidden by Options directive, or specific module errors.

Common log messages explained:

  • "Permission denied: access to / denied" → file permissions issue
  • "Options FollowSymLinks and SymLinksIfOwnerMatch are both off" → .htaccess or server config restricting symlinks
  • "client denied by server configuration" → .htaccess or virtual host rules blocking access
  • "AH01630: client denied by server configuration" → Apache 2.4 syntax for access denial

These logs usually point you directly to the problem.

When to contact support

You've checked permissions, ownership, .htaccess, firewall rules, virtual hosts, and CDN settings. You've read the logs. Nothing works. Now it's time to contact support if you have managed support with your VPS provider.

Before you do, gather information. You'll need the exact error message, the URL that's returning 403, recent changes you made to the server, relevant error log entries, and your current IP address.

Good support can check server level configs you don't have access to. This includes the main Apache or Nginx config, SELinux or AppArmor settings if enabled, or mod_security rules that might be too aggressive.

Check SELinux issues

If you have root access and you're on RHEL, CentOS, or Fedora, check if SELinux is causing issues:

“`bash title="Show the current SELinux enforcement mode" getenforce


If it returns Enforcing, SELinux might be blocking access even with correct permissions.

Check SELinux contexts on your files:

```bash title="Inspect SELinux contexts on the web root"
ls -Z /var/www/html

You should see contexts like httpdsyscontent_t for web files. If they're wrong, restore the correct contexts:

“`bash title="Restore SELinux contexts on the web root" restorecon -R /var/www/html


To temporarily disable SELinux for testing:

> **Safety check:** Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.

```bash title="Set SELinux to permissive mode temporarily"
setenforce 0

If that fixes it, SELinux file contexts need adjustment. Note that this change won't survive a reboot. For permanent changes, you need to configure SELinux policies properly or disable it in /etc/selinux/config.

But this is advanced territory. If you're not familiar with SELinux, contact support rather than permanently disabling it. SELinux provides important security benefits when configured correctly.

Check AppArmor issues (Ubuntu users)

Ubuntu uses AppArmor instead of SELinux. If you're getting unexplained 403 errors on Ubuntu, AppArmor might be the culprit.

Check if AppArmor is enabled:

“`bash title="Show AppArmor profile status" sudo aa-status


This shows which profiles are loaded and in enforce mode. Look for profiles related to Apache or Nginx.

Check AppArmor logs for denials:

```bash title="Search syslog for AppArmor denials"
sudo grep -i apparmor /var/log/syslog | grep DENIED

If you see denials related to your web server, you can temporarily put the profile in complain mode:

“`bash title="Put the Apache AppArmor profile in complain mode" sudo aa-complain /etc/apparmor.d/usr.sbin.apache2


or for Nginx:

```bash title="Put the Nginx AppArmor profile in complain mode"
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx

If your site works in complain mode, the AppArmor profile needs adjustment. This is complex territory, so contact support or consult AppArmor documentation.

Preventing future 403 errors

Once you've fixed it, let's make sure it doesn't happen again.

Document your permission scheme. When you set up a site, note down what user and group owns the files and what permissions they have. When you migrate or restore from backup, you'll know exactly what to set.

Be careful with .htaccess edits. Before modifying it, make a backup:

“`bash title="Back up .htaccess with today's date" cp .htaccess .htaccess.backup-$(date +%F)


Test changes on a staging environment first, not production.

Update your CMS and plugins regularly. Old plugins with security issues get patched, and sometimes those patches change permission requirements or add stricter access controls. After major WordPress updates, verify that your file permissions haven't been reset.

Monitor fail2ban activity. Occasionally review banned IPs to make sure legitimate users aren't getting blocked:

```bash title="Review one Fail2Ban jail's status"
fail2ban-client status [jail-name]

If you see your own IP repeatedly banned, adjust your fail2ban threshold or whitelist your IP.

Keep documentation of your DNS records. When you change hosting or move servers, you'll know exactly what records to update. A simple text file with your A records, MX records, and nameservers saves hours of troubleshooting.

Use version control for config files. Keep your .htaccess, Nginx conf files, and other configs in a git repo. When something breaks, you can diff against the last working version and find exactly what changed.

Review mod_security logs periodically if you have it enabled. Look for false positives that might be blocking legitimate users and adjust rules accordingly.

Test virtual host configs after adding new sites. Run:

“`bash title="Review enabled Apache virtual hosts" apache2ctl -S


or

```bash title="Validate Nginx before restarting"
nginx -t

Use these checks to verify your configuration before restarting the web server.

Set up monitoring. Use a service like UptimeRobot or StatusCake to alert you immediately when your site returns a 403. The faster you catch issues, the less impact they have.

Ready to host websites where you control the 403 fix?

Use Virtarix website VPS resources for sites where file permissions, web server config, logs, root access, NVMe storage, snapshots, and backups are under your control.

VPS S

For small websites and landing pages

$ 5 .50 /month
  • 3 cores
  • 6 GB
  • 50 GB NVMe
  • Unlimited
Get It Now
BEST SELLER

VPS M

For growing sites and staging

$ 11 .40 /month
  • 6 cores
  • 16 GB
  • 100 GB NVMe
  • Unlimited
Get It Now
Peter French
About the Author Peter Frenchis the Managing Director at Virtarix, with over 17 years in the tech industry. He has co-founded a cloud storage business, led strategy at a global cloud computing leader, and driven market growth in cybersecurity and data protection.