linux

Fix Nginx 403 Forbidden Error On Ubuntu

Encountering a 403 Forbidden error on an Ubuntu server running Nginx means the server understands your request but refuses to fulfill it. Unlike a missing file that triggers a 404, a 403 points straig

Fix Nginx 403 Forbidden Error On Ubuntu
In this story

Encountering a 403 Forbidden error on an Ubuntu server running Nginx means the server understands your request but refuses to fulfill it. Unlike a missing file that triggers a 404, a 403 points straight to a block wall: permissions, missing index files, or restricted server blocks. When this happens, you don't need to guess or randomly run chmod commands.

To resolve the issue quickly, check /var/log/nginx/error.log first. The log entry will immediately tell you whether Nginx is tripping over Permission denied or complaining about a missing index file.


Diagnosing the Root Cause via Error Logs

Before changing any configuration files or file permissions, look at the actual error recorded by the Nginx worker process. By default, Nginx logs operational failures to a specific log file on Ubuntu systems.

Run this command in your terminal to watch live error output:

sudo tail -f /var/log/nginx/error.log

While watching the output, try loading the page in your browser. You will typically see one of two common messages:

  • open() "/var/www/html/index.html" failed (13: Permission denied)
  • directory index of "/var/www/html/" is forbidden

Identifying the exact error string lets you apply the correct remedy without breaking your server's security posture.


Fixing File and Directory Permissions

The most frequent culprit behind an Nginx 403 error on Ubuntu is incorrect file ownership or restrictive mode bits. Nginx worker processes run under a dedicated unprivileged user account, which is typically www-data on Ubuntu and Debian distributions. If www-data cannot read your web root files or traverse the parent directory path, Nginx returns a 403 status.

Adjusting Ownership

Ensure that your web root files and directories are owned by the www-data user and group. For example, if your site files live in /var/www/html, run:

sudo chown -R www-data:www-data /var/www/html

Correcting Permission Bits

Next, enforce standard web permissions across your project directory. Directories need execute permissions (+x) so the worker process can traverse into them, while static files need standard read permissions:

sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;

Avoid using chmod 777 as a quick fix. Granting universal write access introduces severe security vulnerabilities and rarely solves underlying traversal issues.


Checking Parent Directory Traversal with Namei

Sometimes file permissions look correct, but a parent directory in the path lacks execution rights. For example, if you serve files out of a user's home directory or a custom path, Nginx might be blocked several levels up.

You can inspect the entire permission chain using the namei utility:

namei -l /var/www/html/index.html

Examine the output columns. Every directory in the path must have the read and execute bits set for others (or the specific group running Nginx) so the worker process can reach the destination file.


Resolving Missing Index Files and Directory Indexing

If your error log points to a forbidden directory index, Nginx is attempting to list the contents of a directory where no default index file exists, and directory listing is turned off.

Adding a Default Index File

Check your web root to confirm whether an index.html, index.php, or similar file exists:

ls -la /var/www/html

If the file is missing, create a basic placeholder file or restore your application entry point.

Configuring Nginx Server Blocks

Open your site configuration file located in /etc/nginx/sites-available/ (or /etc/nginx/nginx.conf) and verify the index directive inside your location block:

server {
    listen 80;
    server_name example.com;

    root /var/www/html;
    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ =404;
    }
}

If you intentionally want Nginx to display file lists when an index file is missing, enable directory indexing explicitly by adding autoindex on; inside the location block, though this is rare for production websites. Always test your configuration changes before reloading:

sudo nginx -t
sudo systemctl reload nginx

FAQ

Why does Nginx give a 403 instead of a 404 when a file is missing?

If your try_files directive falls back to a directory check (for example, using $uri/) and no index file is present inside that directory, Nginx treats the request as an attempt to view a directory listing, resulting in a 403 error rather than a 404.

Is www-data always the user Nginx runs as on Ubuntu?

Yes, default Ubuntu and Debian packages configure Nginx worker processes to run as www-data. You can verify this by checking the user directive at the very top of /etc/nginx/nginx.conf.

Can AppArmor cause a 403 error on Ubuntu?

While rare because standard Ubuntu Nginx packages do not enforce strict confinement profiles by default, a custom or hardened AppArmor profile can block Nginx file reads. You can check for recent denials by running sudo dmesg | grep -i 'apparmor.*DENIED'.

How do I verify my complete merged Nginx configuration?

Complex setups use many include directives across multiple files. Running sudo nginx -T prints the fully merged configuration to your terminal, making it easy to spot conflicting deny rules or misconfigured root paths. Learn more about managing web servers through the Nginx Official Documentation.

SB

Written by

Samuel Baker

Samuel Baker approaches each story with intellectual curiosity and a commitment to fairness, earning the trust of readers and sources alike.

More from linux