Your home page loads, but every other page, such as /about/ or /blog/my-post, returns 404 Not Found. On Apache this was handled by an .htaccess file. Nginx ignores .htaccess, so it looks for a real file called /about/ on disk, does not find one, and gives up. The fix is a single try_files line that sends those requests to your app. This guide gives the right line for common apps and covers the other usual causes.
Before you start
- A Linux VPS running Nginx.
- SSH access with sudo.
- The site's server block file, for example
/etc/nginx/sites-available/example.comor/etc/nginx/conf.d/example.com.conf.
How try_files works
try_files checks a list of options in order and uses the first one that exists. The last item is the fallback:
try_files $uri $uri/ /index.php?$args;This reads: serve the file if it exists, else the folder, else pass the request to /index.php with the original query string. Your app's router then reads the original URL and shows the right page.
The default many configs ship with ends in =404, which is right for a plain static site but wrong for apps that route URLs themselves.
WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}Then, in WordPress, go to Settings → Permalinks and click Save Changes once. WordPress does not need to write a rules file on Nginx, but saving refreshes its own routing.
For WordPress in a subfolder such as /blog/:
location /blog/ {
try_files $uri $uri/ /blog/index.php?$args;
}More in Fix WordPress permalinks 404 on Nginx.
Laravel and most PHP frameworks
The web root must be the public folder, not the project folder:
root /var/www/myapp/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}Symfony, Slim, CodeIgniter 4 and similar frameworks use the same pattern with their own front controller.
Single-page apps (React, Vue, Angular)
A built single-page app has one index.html and handles routes in the browser. Every unknown path should get that file:
root /var/www/app/dist;
location / {
try_files $uri $uri/ /index.html;
}If the app also calls an API under /api/, give the API its own location so missing API routes return real 404s instead of the HTML page:
location /api/ {
proxy_pass http://127.0.0.1:3000;
}Static sites with clean URLs
For a static site where /about should serve about.html:
location / {
try_files $uri $uri.html $uri/ =404;
}root vs alias mistakes
Inside a location, root adds the full URL path to the folder, and alias replaces the location prefix:
location /static/ {
root /var/www/app;
}A request for /static/logo.png is served from /var/www/app/static/logo.png.
location /static/ {
alias /var/www/app/assets/;
}The same request is served from /var/www/app/assets/logo.png.
Common mistakes:
- Using
root /var/www/app/static;inlocation /static/makes Nginx look in/var/www/app/static/static/. - Missing trailing slash: with
alias, if the location ends in/, the alias path must too.
The error log shows the exact path Nginx tried:
sudo tail -n 20 /var/log/nginx/error.log | grep "No such file"Check which server block answered
A 404 on every page, including the home page, often means a different server block is answering, for example the default site. Check which server_name blocks exist for the port:
sudo nginx -T | grep -E "server_name|listen"Then compare the error in your site's own log with the default log. If requests land in the default log, the server_name does not match the domain or the site is not enabled. See Fix Nginx showing the default page instead of your site.
Apply the change
sudo nginx -t && sudo systemctl reload nginxCheck it works
Request a pretty URL directly:
curl -I https://example.com/about/You want 200. A real missing page should still return a 404 from your app, which you can see in the body:
curl -s https://example.com/this-does-not-exist | head -n 20The app's own "page not found" design means routing works. A plain white Nginx 404 page means the request never reached the app.
Troubleshooting
- Home page works, all other pages 404:
try_filesends in=404or is missing. Use the app's fallback line. - PHP files download instead of running after the change: the PHP
location ~ \.php$block is missing or broken. See PHP files downloading instead of running. - CSS and images 404 but pages work: a
rootoraliaspath is wrong in the static location. Check the path in the error log. - Laravel shows a 404 for every route:
rootpoints at the project folder, notpublic. - SPA works until you refresh a page: the fallback to
/index.htmlis missing.
Next steps
- Coming from Apache rewrite rules: Nginx 301 redirects and rewrite rules explained.
- A full single-page app setup: Host a React or Vue app with Nginx.
