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.
| Symptom | Likely Cause | First Check |
|---|---|---|
| Homepage works, subpages return 404 | Missing application fallback or incorrect try_files | Check the location / block and whether the app needs /index.html or /index.php fallback. |
| Only one hostname returns 404 | Wrong server_name or default server block handling the request | Send a local request with the same Host header and inspect active server blocks. |
| Static assets return 404 after deployment | Wrong document root, missing build output, bad asset path, or wrong alias | Check 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 fallback | Check the PHP location and the application document root. |
| API path returns 404 behind Nginx | Backend route mismatch or proxy_pass URI rewriting changed the path | Request 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.
| Directive | Context | Role in 404 Troubleshooting |
|---|---|---|
server_name | server | Matches the requested host name to the intended virtual server. |
listen | server | Controls the address, port, and optional default server for that listener. |
root | http, server, location, if in location | Builds a filesystem path by appending the request URI to the configured root. |
alias | location | Replaces the matching location prefix with another filesystem path. Trailing slashes matter. |
index | http, server, location | Controls which file Nginx looks for when a request maps to a directory. |
try_files | server, location | Checks files or directories in order, then internally redirects or returns a status code when nothing matches. |
location | server, location | Selects which block handles the request. Exact, prefix, ^~, and regex locations can change the result. |
error_page | http, server, location, if in location | Can route 404 responses to a custom page, named location, or application fallback. |
proxy_pass | location, if in location, limit_except | Forwards requests to an upstream and changes URI handling when the upstream URL includes a URI. |
fastcgi_param | http, server, location | Passes 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 Type | Example | 404 Risk |
|---|---|---|
| Exact match | location = /health | Only handles that exact URI. A nearby path such as /health/ uses another block. |
| Prefix match | location /app/ | Handles matching prefixes unless a regex location wins later. |
| Protected prefix | location ^~ /assets/ | Stops later regex locations from overriding the static asset path. |
| Regex match | location ~ \.php$ | Can override prefix handling when the request matches the regex. |
| Named location | location @app | Only 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
aliasinside regex locations. Nginx requires captures when a regex location usesalias, 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, orbuild. - 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 -tbefore 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.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><a href="https://example.com">link</a><blockquote>quote</blockquote>