How to Fix React Router, Vue & SPA 404 on Reload with Nginx try_files
When users visit client-side routes directly or reload the page in React, Vue, Angular, or Vite apps, Nginx looks for a physical directory on the disk and returns a 404 Not Found error. Configuring try_files falls back to index.html so client routers can handle URL rendering.
Interactive Nginx Config Generator
server {
listen 80;
server_name example.com;
root /var/www/myapp/dist;
index index.html;
# Enhanced Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
# Check for static file, directory, or fallback to index.html
try_files $uri $uri/ /index.html;
}
# Cache static assets (JS, CSS, images) with long TTL
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|webp|avif)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
# Ensure index.html is NEVER cached so updates appear immediately
location = /index.html {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}Directives & Architecture Explained
try_files $uri $uri/ /index.html;Tests if $uri is an existing file, if not checks if it is a directory, and if neither exists, silently rewrites to /index.html.
expires 1y;Sets client-side caching header for hashed JavaScript bundles, stylesheets, and fonts for maximum performance.
location = /index.html { add_header Cache-Control "no-store"; }Prevents browsers from caching index.html, ensuring users always fetch new script asset hashes after new deployments.
Production Verification & Reload Workflow
sudo nginx -tVerify try_files and regex location blocks for syntax correctness
curl -I http://myapp.com/dashboardConfirm client route returns HTTP 200 OK with text/html content instead of 404
Production Troubleshooting Tips
- β’Ensure root points to the built production folder (e.g. /dist or /build) containing index.html, not the source code root.
- β’Check file system read permissions for www-data: chmod -R 755 /var/www/myapp/dist.
- β’If you have API routes on the same domain, place location /api/ { proxy_pass ... } ABOVE the location / { try_files ... } block.
Frequently Asked Questions
Why does React Router break on page reload?
Browsers request /dashboard from Nginx directly. Since /dashboard is not a physical file on the server disk, Nginx returns 404 unless try_files instructs it to serve index.html.
Will this hurt SEO for static content?
For pure SPAs, search bots will parse index.html and run client-side JavaScript. For SSR (Server Side Rendering), use Next.js or Nuxt with a reverse proxy instead.