Frontend & SPAsspa.conf

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

spa.conf
/etc/nginx/sites-available/spa.conf
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 -t

Verify try_files and regex location blocks for syntax correctness

curl -I http://myapp.com/dashboard

Confirm 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.