The Nginx 403 Forbidden error when trying to access a directory means the web server understands your request but refuses to fulfill it because it doesn’t have permission to show you the contents of that specific directory.

Here are the most common reasons and how to fix them:

1. Incorrect File Permissions

This is the most frequent culprit. Nginx, by default, runs as a specific user (often nginx or www-data). This user needs read and execute permissions on the directory and its parent directories to list its contents.

Diagnosis:

ls -ld /path/to/your/directory
ls -ld /path/to/your
ls -ld /path/to

Look at the output. The user Nginx runs as (e.g., www-data) must have r (read) and x (execute) permissions. For example, if Nginx runs as www-data, you might see something like drwxr-xr-x. The x on the directory itself is crucial for listing its contents.

Fix:

sudo chmod 755 /path/to/your/directory
sudo chmod 755 /path/to/your
sudo chmod 755 /path/to

If Nginx runs as a different user, replace 755 with appropriate permissions for that user, ensuring they have at least r and x on the directory. The 755 permission grants read and execute to everyone, and write only to the owner, which is generally safe and common. This allows Nginx to traverse the directory tree and list the directory’s contents.

2. Missing index.html or index.htm and autoindex Disabled

Nginx tries to serve an index file (index.html, index.htm, etc.) by default when a directory is requested. If no such file exists and autoindex is disabled, Nginx will return a 403.

Diagnosis: Check your Nginx configuration file (e.g., /etc/nginx/sites-available/your_site or /etc/nginx/nginx.conf). Look for the index directive within the server or location block.

location / {
    index index.html index.htm;
    # ... other directives
}

Then, check if autoindex is explicitly turned off or not present (which defaults to off).

location / {
    # ...
    autoindex off; # or not present
}

Also, verify that the actual index file you expect (e.g., index.html) is present in /path/to/your/directory and has correct permissions.

Fix: Option A (Add an index file): Create a simple index.html file in the directory:

echo "<h1>Welcome!</h1>" | sudo tee /path/to/your/directory/index.html
sudo chown www-data:www-data /path/to/your/directory/index.html # Adjust user/group if needed

This provides Nginx with a file to serve, satisfying the request.

Option B (Enable autoindex): If you want Nginx to list the directory contents, explicitly enable autoindex in your Nginx configuration:

location / {
    index index.html index.htm;
    autoindex on;
}

Reload Nginx: sudo systemctl reload nginx. This tells Nginx to generate a directory listing if no index file is found, allowing users to browse the directory contents.

3. Incorrect root or alias Directive

The root or alias directive in your Nginx configuration specifies the base directory from which Nginx serves files. If this path is incorrect, Nginx might be looking in the wrong place, leading to a 403.

Diagnosis: Examine your Nginx configuration for the server or location block handling the problematic URL.

server {
    listen 80;
    server_name example.com;
    root /var/www/html; # Or alias /some/other/path;

    location /my_app {
        # ...
    }
}

Ensure the root path or the path specified in alias actually exists and contains the files/directories you intend to serve. For example, if your root is /var/www/html and you’re requesting http://example.com/my_app/, Nginx will look for /var/www/html/my_app/.

Fix: Correct the root or alias directive to point to the actual directory containing your website’s files.

server {
    listen 80;
    server_name example.com;
    root /var/www/correct_path; # Corrected path
    # ...
}

Reload Nginx: sudo systemctl reload nginx. This ensures Nginx is looking in the right filesystem location for your content.

4. SELinux or AppArmor Restrictions

Security modules like SELinux (on RHEL/CentOS/Fedora) or AppArmor (on Ubuntu/Debian) can prevent Nginx from accessing files and directories even if standard Unix permissions are correct.

Diagnosis:

  • SELinux: Check the audit log for denials: sudo ausearch -m avc -ts recent. Look for messages related to nginx or httpd trying to access your web directory.
  • AppArmor: Check the system logs (e.g., /var/log/syslog or /var/log/audit/audit.log) for AppArmor denials related to Nginx.

Fix:

  • SELinux: If you confirm SELinux is the issue, you might need to adjust the security context of your web root directory. For common web content:
    sudo semanage fcontext -a -t httpd_sys_content_t "/path/to/your/directory(/.*)?"
    sudo restorecon -Rv /path/to/your/directory
    
    If this is a temporary measure or you’re unsure, you can temporarily set SELinux to permissive mode (sudo setenforce 0), test, and then re-enable it (sudo setenforce 1) to confirm the problem.
  • AppArmor: Edit the Nginx AppArmor profile (e.g., /etc/apparmor.d/usr.sbin.nginx) to allow access to the directory. Reload AppArmor: sudo systemctl reload apparmor.

5. deny all in Location Block

An explicit deny all; directive within a location block can prevent access to specific paths or the entire site if misconfigured.

Diagnosis: Review your Nginx configuration file for any location blocks that might be inadvertently blocking access.

location /private {
    deny all; # This would block access to /private
}

Fix: Remove or comment out the deny all; directive if it’s not intended to block access to the directory you’re trying to reach.

location /private {
    # deny all; # Commented out
}

Reload Nginx: sudo systemctl reload nginx. This removes the explicit restriction, allowing Nginx to process the request based on other directives.

6. Incorrect client_body_temp_path Permissions

While less common for directory indexing errors specifically, if Nginx is trying to write temporary files (e.g., during uploads or complex proxying) and lacks permissions on its temporary directory, it can manifest in various errors, including 403s if the context implies a permission issue.

Diagnosis: Check your nginx.conf or server block for client_body_temp_path.

http {
    client_body_temp_path /var/lib/nginx/body;
    # ...
}

Ensure this directory exists and the Nginx user has write permissions.

Fix:

sudo mkdir -p /var/lib/nginx/body
sudo chown www-data:www-data /var/lib/nginx/body # Adjust user/group if needed
sudo chmod 700 /var/lib/nginx/body

This ensures Nginx can create temporary files if needed.

After applying these fixes, remember to reload Nginx using sudo systemctl reload nginx or sudo service nginx reload. If you fix all these, the next error you’ll likely encounter is a 502 Bad Gateway if your Nginx is acting as a reverse proxy and the upstream application is down.

Want structured learning?

Take the full Nginx course →