Ffile2fix
Sign in Get started

Fix "403 Forbidden" on Apache and Nginx

Forbidden
You don't have permission to access this resource.

A 403 means the server understood the request but refuses to serve it. The usual reasons are file permissions the web server cannot read, a directory with no index file and listing disabled, or an access rule (Require, deny, WAF) blocking the request.

Also appears as: 403 Forbidden nginx · directory index of "/var/www/html/" is forbidden · AH01630: client denied by server configuration: /var/www/html/ · open() "/var/www/html/index.html" failed (13: Permission denied)

Common causes

  • Files or parent directories are not readable or traversable by the web server user
  • The directory has no index.php or index.html and directory listing is disabled
  • Apache Require all denied, an old Order/Deny rule or an .htaccess IP block
  • A security plugin, ModSecurity rule or CDN firewall blocking the request
  • SELinux context on RHEL-based systems not set to httpd_sys_content_t
  • Files uploaded outside the DocumentRoot or root path configured for the site

How to fix it

  1. Read the error log entry. Apache logs AH01630 (config deny), AH01276 (no index) or AH00035 (permissions); Nginx logs "directory index ... is forbidden" or "(13: Permission denied)". Each points to a different fix.
  2. Fix permissions on the whole path. Directories need 755 and files 644, and every parent directory must be traversable. Check the chain with namei -l /var/www/html/index.php.
  3. Add an index file or index directive. Make sure index.php or index.html exists, and that DirectoryIndex (Apache) or index index.php index.html (Nginx) lists it.
  4. Review access rules. In Apache 2.4 the site Directory block needs Require all granted. Look in .htaccess for deny from, Require ip or Require not lines that match your IP.
  5. Check security layers. Temporarily disable the security plugin, look in ModSecurity's audit log, or check the CDN firewall events for blocked requests from your IP.
  6. Fix SELinux labels. On RHEL, Alma or Rocky run sudo restorecon -Rv /var/www/html. For custom paths add a rule with semanage fcontext -a -t httpd_sys_content_t "/srv/site(/.*)?" and run restorecon.

Shell + Apache config

sudo find /var/www/site -type d -exec chmod 755 {} +
sudo find /var/www/site -type f -exec chmod 644 {} +
namei -l /var/www/site/index.php

<Directory /var/www/site>
    AllowOverride All
    Require all granted
</Directory>

How to stop it happening again

  • Deploy with a script that sets ownership and permissions consistently
  • Never fix a 403 with chmod 777; it creates a security hole and can cause 500 errors under suEXEC
  • Document IP allowlists so they are updated when office IPs change
  • Keep SELinux labels correct with semanage rules rather than disabling SELinux

Frequently asked questions

What is the difference between 401 and 403?

401 means you need to authenticate and may retry with credentials. 403 means the server refuses even if you are authenticated.

Why does chmod 777 not fix it?

If the problem is an access rule, missing index or SELinux, permissions are not the cause. On suEXEC or PHP-FPM setups, world-writable files can even be refused, so use 755/644.

Why do only some visitors get 403?

Usually a firewall, WAF or geo/IP block is matching their IP, country or user agent. Check the firewall or CDN event log for their requests.