एरर समाधान · सर्वर एरर

nginx 404 for Files That Exist: root, alias and try_files Fixes

nginx returns 404 although the file is on disk? Check root versus alias, location matching, try_files order, case, symlinks and the wrong server block, using the error log.

Error messageopen() "/usr/share/nginx/html/style.css" failed (2: No such file or directory)

लेखक: SiteDownCheck संपादकीय टीम · अपडेट: अक्टूबर 28, 2026 · 4 मिनट पढ़ने में

You can see the file on the server, yet nginx answers 404 Not Found. The error log shows the path nginx actually tried, and that path is almost never the one you expected:

[error] 812#812: *5 open() "/usr/share/nginx/html/assets/app.css" failed (2: No such file or directory), client: 203.0.113.9, server: example.com, request: "GET /assets/app.css HTTP/1.1"

Here the file lives under /var/www/example.com/assets/app.css, but nginx looked in the packaged default folder, which means a different server block or location handled the request. The rule for debugging this: compare the path in the log line with the path on disk, character by character, and work out which directive produced it.

If the 404 is on pages of a PHP app and the log says FastCGI sent in stderr: "Primary script unknown", jump to the PHP section.

Step 1: is the right server block answering?

sudo nginx -T | grep -n -E "server_name|listen|root"
curl -I -H "Host: example.com" http://127.0.0.1/assets/app.css

The default /usr/share/nginx/html or /var/www/html path in the error log is a strong sign that a default server caught your request because the Host did not match any server_name, or your new vhost file is not enabled (sites-enabled symlink missing) or not loaded (no matching include). Fix server_name, enable the file, and sudo nginx -t && sudo systemctl reload nginx.

Step 2: how root builds the path

With root, nginx appends the full request URI to the path:

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

A request for /static/app.css is read from /var/www/app/static/app.css. If the files are really in /var/www/app/app.css or /var/www/app/public/, you get 404.

Step 3: alias replaces the location prefix

location /static/ {
    alias /var/www/app/public/;
}

Here /static/app.css maps to /var/www/app/public/app.css. The matched prefix is replaced by the alias path. The classic bug is a trailing slash mismatch: if the location ends in /, the alias must also end in /, or you get paths like /var/www/app/publicapp.css. Prefer root unless you need a different name for the URL prefix.

Step 4: try_files order and fallback

location / {
    try_files $uri $uri/ /index.html;
}

nginx tries each entry in order and uses the last one as an internal redirect (or =404). The patterns that cause trouble:

Static assets should fail properly instead of returning your SPA shell with a 200, which hides broken links. Give them their own block:

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

Step 5: a regex location stole the request

Regex locations (~, ~*) take priority over plain prefix matches unless the prefix uses ^~. A broad rule like location ~* \.(css|js)$ { root /var/www/other; } can capture assets before your intended block. Make the intended location win:

location ^~ /static/ {
    alias /var/www/app/public/;
}

Print the config in order and check which location is the best match for the URL.

Linux file names are case sensitive: Logo.PNG is not logo.png. Files created on Windows or macOS and copied over often differ in case from what the HTML requests. Symlinked deploy folders (current pointing at a release) work, but a stale link pointing to a deleted release gives 404 for everything. Check ls -l on the link target. If you use disable_symlinks, nginx will treat linked paths as not found or forbidden.

Step 7: PHP "Primary script unknown"

For .php files the path is built by FastCGI parameters:

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

If $document_root points at a folder PHP-FPM cannot see (a container with a different mount, or chroot), PHP says "Primary script unknown" and nginx returns 404. The path must be valid from the PHP-FPM process's point of view. See also nginx upstream sent too big header if the response fails later.

Step 8: client-side check

After the server side is fixed, confirm what clients receive. The HTTP स्टेटस चेकर shows the final status. Use the ब्रोकन लिंक चेकर to find remaining 404s across the site, and the HTTP Header Checker to make sure a CDN is not serving a cached 404. Purge the CDN cache if it is. An अपटाइम मॉनिटर on a static asset URL and a deep link catches regressions.

अगले कदम

सामान्य प्रश्न

What is the difference between root and alias?

root appends the whole URI to its path; alias replaces the matched location prefix with its path. Use root when URL and folder names match.

Why does my SPA return 404 on refresh?

Deep links are not real files. Use try_files $uri $uri/ /index.html; so unknown paths load the app, which then handles routing.

How can I see which location handled a request?

Raise the log level temporarily with error_log ... debug (needs a debug-enabled build) or add add_header X-Debug "api-location" always; to each block and inspect the headers.

Is the 404 coming from nginx at all?

Check the error page. A custom app 404 or a CDN page means another layer answered. Compare with a direct request to the origin.

Get notified when this fix updates

Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.

यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.