Nginx

Nginx alias vs root Directive Causing 404 on Static Files

· by DebuggedIt

Quick answer

root and alias both map a request path to a filesystem location, but they combine the matched location path with the filesystem path differently — mixing up...

root and alias both map a request path to a filesystem location, but they combine the matched location path with the filesystem path differently — mixing up which one you need for a given location block is one of the most common causes of static files returning 404 despite clearly existing on disk.

The Problem

Static files served from a specific subpath return 404, even though the files genuinely exist at the expected location on disk:

location /static/ {
    root /var/www/assets;
}

A request to /static/logo.png returns 404, even though a file exists at /var/www/assets/logo.png — the file is there, but Nginx is looking in the wrong place.

root vs alias path combination root: appends full request path root /var/www/assets; /static/logo.png -> /var/www/assets/static/logo.png alias: replaces the location prefix alias /var/www/assets/; /static/logo.png -> /var/www/assets/logo.png

Why It Happens

The two directives combine the location block's matched path with the configured filesystem path in fundamentally different ways:

  • root appends the entire request URI (including the part matched by the location block itself) to the configured path — so root /var/www/assets; inside location /static/ looks for the file at /var/www/assets/static/logo.png, including the /static/ segment, since root doesn't strip anything from the original request path
  • alias replaces the matched location prefix entirely with the configured path — so alias /var/www/assets/; inside that same location /static/ looks for the file at /var/www/assets/logo.png, since the /static/ portion of the request is effectively substituted away, not appended onto

Using root when you actually needed alias (or vice versa) is precisely what produces this exact 404 pattern — the file exists, but at a path one directory level different from where Nginx is actually looking, because of this fundamental difference in how each directive combines paths.

The Fix

If your filesystem structure mirrors your URL structure directly (files are genuinely organized under a matching static subdirectory on disk), use root with a base path that, combined with the full request URI, correctly resolves:

location /static/ {
    root /var/www/assets;
    # correctly resolves /static/logo.png to /var/www/assets/static/logo.png
    # meaning the actual file needs to exist at that full path, including "static"
}

If your filesystem structure does NOT include the URL prefix as a literal subdirectory (files live directly in the target directory, without an extra static folder layer), use alias instead, which correctly strips the /static/ prefix before looking on disk:

location /static/ {
    alias /var/www/assets/;
    # correctly resolves /static/logo.png to /var/www/assets/logo.png
}

Note the trailing slash on the alias path is generally important and should match the trailing slash on the location block itself — a mismatch in trailing slashes between location and alias is a related, additional common source of subtly broken paths, distinct from the root-vs-alias confusion itself but often encountered alongside it.

To definitively confirm which directive you actually need for a specific case, check where the files genuinely live on disk relative to your location block's matched prefix, and choose accordingly — if the location prefix (like static) is itself a literal directory name on disk, root is usually correct; if it's purely a URL-level organizational prefix, with no corresponding directory on disk, alias is usually what you need.

When debugging exactly which path Nginx is actually trying to serve, temporarily enable more verbose logging to see the resolved filesystem path Nginx attempted for a specific failing request:

error_log /var/log/nginx/error.log notice;

Nginx's error log for a 404 on a static file typically includes the exact filesystem path it attempted to open, which immediately clarifies whether it's looking in the wrong place due to a root/alias mismatch, or whether the file genuinely doesn't exist at the expected location at all for some other reason.

Still Not Working?

If you've corrected the root/alias usage but files still 404, double-check file permissions on both the file itself and every parent directory in the path, since the Nginx worker process needs execute (traverse) permission on every directory in the chain leading to the file, not just read permission on the file itself — a correctly resolved path with insufficient directory permissions produces a 403 in some configurations, but can also present as an effective 404 in others depending on your specific Nginx version and configuration, making this worth checking even when the path itself now appears correctly resolved.