How to Fix Nginx 404 Not Found

Fix Nginx 404 Not Found errors by checking server blocks, root and alias paths, try_files, PHP routing, reverse proxies, and logs.

PublishedAuthorJoshua JamesRead time13 minGuide typeNginx

An Nginx 404 Not Found error means the request reached a web server, but the active Nginx configuration or the upstream application could not match it to a real file, directory, route, or fallback handler. The fix depends on where the 404 is generated: static-file lookup, the wrong server block, a bad root or alias, a broken try_files chain, PHP front-controller routing, or a proxied backend returning its own 404.

Start with diagnostics so you can identify the layer returning the 404 before changing a live site. The common fixes cover static sites, PHP applications, single-page applications, reverse proxies, and packaged Linux layouts. Run the checks from the Nginx host where possible, because local requests remove DNS, CDN, and browser cache noise from the first pass.

Understand Nginx 404 Not Found Causes

Nginx can return a 404 by itself, or it can pass a 404 returned by PHP, Node.js, Python, another upstream service, or a CDN layer in front of the origin. A generic browser message does not identify which layer made the decision, so start with logs and a local request against the exact hostname and path.

SymptomLikely CauseFirst Check
Homepage works, subpages return 404Missing application fallback or incorrect try_filesCheck the location / block and whether the app needs /index.html or /index.php fallback.
Only one hostname returns 404Wrong server_name or default server block handling the requestSend a local request with the same Host header and inspect active server blocks.
Static assets return 404 after deploymentWrong document root, missing build output, bad asset path, or wrong aliasCheck the resolved filesystem path from the error log and confirm the file exists there.
PHP routes return 404 or “No input file specified”Wrong SCRIPT_FILENAME, root points above or below public, or missing front-controller fallbackCheck the PHP location and the application document root.
API path returns 404 behind NginxBackend route mismatch or proxy_pass URI rewriting changed the pathRequest the upstream directly and compare it with the proxied path.

These directives are the usual Nginx 404 decision points. If you understand which one owns the request, most 404 fixes become straightforward.

DirectiveContextRole in 404 Troubleshooting
server_nameserverMatches the requested host name to the intended virtual server.
listenserverControls the address, port, and optional default server for that listener.
roothttp, server, location, if in locationBuilds a filesystem path by appending the request URI to the configured root.
aliaslocationReplaces the matching location prefix with another filesystem path. Trailing slashes matter.
indexhttp, server, locationControls which file Nginx looks for when a request maps to a directory.
try_filesserver, locationChecks files or directories in order, then internally redirects or returns a status code when nothing matches.
locationserver, locationSelects which block handles the request. Exact, prefix, ^~, and regex locations can change the result.
error_pagehttp, server, location, if in locationCan route 404 responses to a custom page, named location, or application fallback.
proxy_passlocation, if in location, limit_exceptForwards requests to an upstream and changes URI handling when the upstream URL includes a URI.
fastcgi_paramhttp, server, locationPasses the resolved script path to PHP-FPM. A wrong SCRIPT_FILENAME often causes PHP-side 404 behavior.

Diagnose an Nginx 404 Not Found Error

Do not start by restarting Nginx. A restart does not fix a wrong path, wrong location block, missing route, or upstream 404. First, confirm the request path, active virtual host, and log message.

Check the Nginx 404 Response Locally

Send a request from the Nginx host with the same hostname and path that fails in the browser. Replace the hostname and path with the affected site:

curl -I -H "Host: example.com" http://127.0.0.1/missing-page

A local 404 confirms that the origin Nginx stack can reproduce the issue without DNS, CDN, or browser cache involved:

HTTP/1.1 404 Not Found
Server: nginx

If the local request works but the public request fails, the issue may sit in DNS, a CDN, a reverse proxy layer, a stale deployment, or a different origin server. In that case, test the public hostname from another network and confirm which origin the edge service is using before editing Nginx.

Read the Nginx Error Log for Missing Files

For static-file 404s, the Nginx error log often tells you the exact filesystem path Nginx tried to open. Most packaged Linux installs use /var/log/nginx/error.log; if that file does not exist, inspect the active error_log directive in sudo nginx -T. The Linux tail command is useful here because recent Nginx errors usually appear at the end of the log.

sudo tail -n 80 /var/log/nginx/error.log

Relevant log lines often look like this:

open() "/var/www/example.com/html/assets/app.css" failed (2: No such file or directory)

That line is more useful than the browser page. It shows the path Nginx expected to exist. If the real file lives somewhere else, fix the Nginx root, alias, deployment path, or application asset URL instead of changing permissions blindly.

Check the Nginx Access Log for the Failing URI

The access log confirms the requested URI and status code. Use a narrow Linux grep command search for 404 responses if the log is busy:

sudo grep ' 404 ' /var/log/nginx/access.log | tail -n 20

Relevant entries include the requested path and final status:

"GET /missing-page HTTP/1.1" 404
"GET /assets/app.css HTTP/1.1" 404

If the path in the log differs from the path you expected, check redirects, base URLs, application asset paths, and CDN rewrite rules before editing the document root.

Find the Active Nginx 404 Configuration

Dump the active Nginx configuration and inspect only the directives that affect routing and file lookup:

sudo nginx -T 2>&1 | grep -E 'server_name|listen|root|alias|index|try_files|location|proxy_pass|fastcgi_param'

Relevant filtered lines might show that the request is using a different document root or fallback than you assumed:

    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com/html;
    index index.html index.htm;
    location / {
        try_files $uri $uri/ =404;

Use this output as a map. The fix should target the block that actually handles the request, not an old disabled file, a copied backup, or a different virtual host.

Fix Nginx 404 Not Found from the Wrong Server Block

A common Nginx 404 happens when the request reaches the wrong server block. This often occurs after adding a new hostname, changing DNS, moving behind a CDN, changing the listener port, or forgetting to include both example.com and www.example.com in server_name.

Test the hostname locally with the exact Host header:

curl -I -H "Host: www.example.com" http://127.0.0.1/

If the wrong site responds, add the missing hostname to the correct server block:

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html index.htm;

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

If you intentionally use separate canonical hostnames, keep redirects in one clear owner. Mixing host redirects across Nginx, a CDN, and an application can produce confusing 404s and loops. For canonical host handling, use the dedicated redirect www or non-www in Nginx workflow rather than spreading redirect logic through several files. If the hostname is correct but the request uses a custom port, check the change ports in Nginx workflow before editing unrelated server blocks.

Fix Nginx 404 Not Found from root and index Paths

The root directive does not point to a specific file. It points to the directory where Nginx starts looking, then Nginx appends the request URI. If root /var/www/example.com/html; handles /assets/app.css, Nginx looks for /var/www/example.com/html/assets/app.css.

Check whether the file exists where Nginx is looking:

sudo test -f /var/www/example.com/html/index.html && echo "index file exists"

Expected output when the file exists:

index file exists

If the check prints nothing, the file is missing at that path. Either deploy the files into the configured root or update Nginx to the real web root.

Correct the Nginx root Directory

Use the directory that actually contains the public web files. Static sites often publish to a dist, build, public, or html directory, and choosing the parent directory can make every asset 404.

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/public;
    index index.html index.htm;

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

After changing root, test one real file path from the server:

sudo test -f /var/www/example.com/public/index.html && echo "root matches deployed files"

Expected output:

root matches deployed files

Correct the Nginx index Directive

If directory requests fail but direct file requests work, check the index directive. A request for / or /docs/ needs an index file inside the resolved directory unless you enable directory listing deliberately.

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/public;
    index index.html index.htm index.php;

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

If the site should never serve directory indexes, keep autoindex disabled and create the expected index file instead. A missing index can look like a routing issue when the real problem is simply that the directory has no default document.

Fix Nginx 404 Not Found from location Matching

Nginx does not always use the block you expect. It checks exact locations, then prefix locations, then regular expressions, with ^~ changing whether regex locations are tested. A broad regex location can intercept assets, PHP files, or application paths and send them to the wrong handler.

Location TypeExample404 Risk
Exact matchlocation = /healthOnly handles that exact URI. A nearby path such as /health/ uses another block.
Prefix matchlocation /app/Handles matching prefixes unless a regex location wins later.
Protected prefixlocation ^~ /assets/Stops later regex locations from overriding the static asset path.
Regex matchlocation ~ \.php$Can override prefix handling when the request matches the regex.
Named locationlocation @appOnly used by internal redirects such as try_files or error_page.

For static assets that must not be handled by an application fallback or regex block, use a clear prefix location:

location ^~ /assets/ {
    root /var/www/example.com/public;
    try_files $uri =404;
}

For an application path, keep the fallback inside the intended location:

location /app/ {
    root /var/www/example.com/public;
    try_files $uri $uri/ /app/index.html;
}

After changing location logic, run a syntax test and check the exact path that previously failed. Do not assume the homepage proves every location block works.

Fix Nginx 404 Not Found from try_files

The try_files directive is often the difference between a working site and a 404 on every deep link. It checks each candidate in order and uses the first match. If nothing matches, the last argument decides the fallback URI, named location, or status code.

Use Nginx try_files for a Static Website

For a normal static site where missing files should be real 404s, keep the fallback explicit:

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

This checks the requested file, then a directory version of the URI, then returns 404 only when neither exists. It is a good default for static HTML sites that do not use client-side routing.

Use Nginx try_files for a Single-Page Application

React, Vue, Angular, Svelte, and other single-page applications often need unknown browser routes to fall back to index.html. Without that fallback, refreshing /dashboard or opening it directly returns an Nginx 404 because there is no physical dashboard file.

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

Only use this pattern when the client application owns those routes. If you apply it to a normal static site, broken URLs may return the app shell with a 200 response instead of a proper 404.

Use Nginx try_files for PHP Front Controllers

WordPress, Laravel, Symfony, CodeIgniter, and many PHP applications route requests through index.php. If Nginx returns =404 before the request reaches the front controller, application routes fail even when the PHP application is healthy.

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

Use the query-string form that matches your application. Some examples use $args, while many framework examples use $query_string. Both carry the request query string, but keeping the application’s documented style makes future maintenance easier.

Fix Nginx 404 Not Found from alias Paths

Use alias when a URI prefix maps to a directory that is not under the main document root. Unlike root, alias replaces the matching location prefix. That difference is a common source of Nginx 404 errors.

This is a correct alias pattern for static files under /assets/:

location /assets/ {
    alias /srv/example-assets/;
}

For a request to /assets/app.css, Nginx looks for /srv/example-assets/app.css. The trailing slash on both the location and alias keeps the path clear, and Nginx returns 404 normally when the mapped file does not exist.

A common mistake is using root where alias was intended:

location /assets/ {
    root /srv/example-assets;
}

That configuration makes Nginx look under a path like /srv/example-assets/assets/app.css, which is wrong when the real file is /srv/example-assets/app.css.

Check the real file path before editing the config:

sudo test -f /srv/example-assets/app.css && echo "asset exists"

Expected output:

asset exists

Be careful with alias inside regex locations. Nginx requires captures when a regex location uses alias, and the alias path must refer to those captures. For most static directory mappings, a simple prefix location is easier to maintain.

Fix Nginx 404 Not Found for PHP and Framework Routes

PHP application 404s usually come from one of two places: Nginx cannot find the physical PHP file, or the application received the request but its router did not match a route. The error page alone may not tell you which one happened.

Correct the Nginx PHP Document Root

Many PHP frameworks expose only a public directory to the web. If Nginx points at the project root instead of public, assets and routes often fail.

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

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

Adjust the PHP-FPM socket path to match your distribution and PHP version. Debian and Ubuntu packages commonly use versioned sockets such as /run/php/php8.3-fpm.sock, while Fedora and RHEL-family packages commonly use /run/php-fpm/www.sock. On Ubuntu systems, finish the socket and pool setup with configure PHP-FPM on Ubuntu. On Fedora systems, use configure Nginx for PHP-FPM on Fedora.

Stop Nginx Passing Missing PHP Files to PHP-FPM

Inside the PHP location, check that the requested PHP script exists before passing it to PHP-FPM. This prevents missing script paths from becoming confusing PHP-side errors.

location ~ \.php$ {
    try_files $uri =404;

    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

If this block returns 404 for a script you expect to exist, check $document_root and the resolved script path first. A valid PHP script should exist under the same root that Nginx uses for the server block. If the status changes to 502, check the PHP-FPM socket or upstream service instead.

Check Nginx and Application Route Ownership

If Nginx forwards the request to index.php and the application still returns 404, the route may not exist inside the application. Confirm by checking application logs after the request. In that case, the Nginx fix is not another rewrite rule; the fix belongs in the application route, base URL, deployment environment, or framework cache.

Fix Nginx 404 Not Found Behind a Reverse Proxy

When Nginx acts as a reverse proxy, a 404 can come from the backend application even though Nginx is healthy. The fastest test is to request the backend directly from the Nginx server, using the same path that fails through the proxy.

curl -i http://127.0.0.1:3000/api/users

If the backend returns 404 directly, fix the backend route or application base path first. If the direct backend request works but the proxied request fails, inspect the location and proxy_pass URI handling.

Preserve or Strip the Nginx Proxy Prefix Deliberately

Decide whether the backend expects the URI prefix. This version preserves /api/ when forwarding to the backend:

location /api/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

With that form, /api/users is sent upstream as /api/users. This version strips the /api/ prefix and sends the remainder to the backend:

location /api/ {
    proxy_pass http://127.0.0.1:3000/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

With that form, /api/users is sent upstream as /users. Either behavior can be correct, but mixing them up produces backend 404s. For broader backend, header, and upstream patterns, use the create a reverse proxy in Nginx workflow.

Fix Nginx 404 Not Found from Deployment and Cache Mismatches

Some Nginx 404s are deployment problems rather than configuration problems. The config points to a valid directory, but the deployed files, generated assets, symlinks, or release directory do not match the URLs being requested.

List the top level of the active document root:

sudo find /var/www/example.com/public -maxdepth 2 -type f | sort | head -n 30

Relevant output should include the files the failing URLs expect:

/var/www/example.com/public/index.html
/var/www/example.com/public/assets/app.css
/var/www/example.com/public/assets/app.js

If the built assets use hashed filenames, check the HTML source and deployment manifest. A page can reference /assets/app.OLDHASH.js after a partial deployment, while the server only contains /assets/app.NEWHASH.js.

If a CDN sits in front of Nginx, purge or bypass the CDN after fixing the origin. A CDN can keep returning an old 404 even after the origin serves 200. Test the origin directly with the host-header diagnostic before changing more Nginx files.

Apply and Verify Nginx 404 Not Found Fixes

After editing an Nginx file, test the syntax before reloading the service:

sudo nginx -t

A successful syntax test prints:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

On systemd-based Linux servers, reload the packaged Nginx service only after the syntax test passes:

sudo systemctl reload nginx

On non-systemd systems, source-built installs, or managed hosting panels, use the reload path that owns that Nginx instance instead of assuming systemctl is available.

Then retest the exact URL that failed. Use curl for a stable header check before returning to the browser:

curl -I -H "Host: example.com" http://127.0.0.1/missing-page

A fixed route should no longer return 404. For a normal successful page, relevant headers include:

HTTP/1.1 200 OK
Server: nginx

If the site should redirect to HTTPS first, verify the redirect and final HTTPS response separately:

curl -I http://example.com
curl -I https://example.com/missing-page

For deeper request testing, the curl command in Linux helps inspect headers, redirects, and response bodies without browser cache affecting the result.

Prevent Repeat Nginx 404 Not Found Errors

Most repeat 404 problems come from drift between application deployment, Nginx paths, and proxy ownership. Keep these checks close to your deployment workflow:

  • Document the active web root for each site, including whether it is html, public, dist, or build.
  • Use one owner for public redirects: Nginx, CDN, proxy manager, or application, not several at once.
  • Test deep links after every single-page application deployment, not just the homepage.
  • Keep static asset paths, build manifests, and release symlinks in the same deployment step.
  • For PHP frameworks, point Nginx to the public web directory, not the full project root.
  • Use sudo nginx -t before every reload and test at least one real content URL afterward.

If a new location, redirect, or rewrite rule caused the 404, keep the fix in the dedicated create rewrite rules in Nginx and redirect URLs in Nginx workflows before expanding the configuration further.

Troubleshoot Common Nginx 404 Not Found Messages

Use the exact log message to choose the next fix. These sample messages avoid timestamps and process IDs because those values are host-specific; look for the stable phrase and path pattern in your own logs.

open() Failed With No Such File or Directory

Relevant log line:

open() "/var/www/example.com/html/assets/app.css" failed (2: No such file or directory)

Nginx tried to open that exact file and it was not there. Check whether the file is missing, deployed to another release directory, referenced with the wrong asset path, or built under a different public directory.

Nginx 404 Only on Refreshed Application Routes

If / loads but /dashboard returns 404 on refresh, the application probably needs a front-controller or single-page application fallback. Static client-side apps usually need try_files $uri $uri/ /index.html;, while PHP front-controller apps usually need a fallback to /index.php.

Nginx 404 for PHP With No input file specified

This usually means PHP-FPM received a script path that does not exist. Check root, SCRIPT_FILENAME, and whether the application expects /public as the document root. Add try_files $uri =404; inside the PHP location so missing scripts fail before PHP-FPM handles them.

Nginx 404 Behind a Proxy but Backend Works Directly

Compare the proxied path with the direct upstream path. The most common cause is proxy_pass URI rewriting under a prefix location. Decide whether the backend expects /api/users or /users, then use the matching proxy_pass form deliberately. If Nginx cannot reach the upstream at all, troubleshoot the related Nginx 502 Bad Gateway error instead.

Nginx 404 After Moving Files to a New Release Directory

Check whether the active symlink or document root still points at the old release. If your deployment uses /var/www/example.com/current, verify the symlink and the files beneath it before changing Nginx:

readlink -f /var/www/example.com/current
sudo test -f /var/www/example.com/current/public/index.html && echo "current release has index"

Example output should show the active release path and confirm the index file exists:

/var/www/example.com/releases/current-build
current release has index

Nginx 404 vs 403 Permission Errors

A true missing-file problem normally logs No such file or directory and returns 404. A permission problem normally logs Permission denied and returns 403. If the browser says 404 but the log says permission denied, check whether a custom error_page, CDN, or application layer is hiding the original status. Do not fix ownership or SELinux labels unless the logs actually point to an access problem.

Conclusion

Fixing an Nginx 404 Not Found error is mostly about identifying the layer that returned it. Check the request locally, read the Nginx logs, confirm the active server block, and then fix the specific mismatch: server_name, root, index, location, alias, try_files, PHP SCRIPT_FILENAME, or reverse proxy URI handling. After every change, run sudo nginx -t, reload through the appropriate service path, and retest the exact URL that failed.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show our tutorials more often in Top Stories and mark them as preferred in AI Mode and AI Overviews when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Add to the discussion

Questions, fixes, command output, and version notes help keep this guide current.

Verify before posting: