← Debugging Guides

阅读中文版本

HTTP 404 Not Found — routing and deployment causes

Diagnose unexpected HTTP 404 in production from SPA fallback, trailing slashes, case sensitivity, and reverse-proxy path rewriting.

beginnerdebugginghttproutingdeployment

Symptoms

  • HTTP 404 on a path that exists in the application
  • It works locally but not on the deployed host
  • Refreshing a client-side route returns 404

Possible causes

  • The SPA fallback does not rewrite unknown paths to index.html
  • A trailing slash or case difference mismatches the route
  • A reverse proxy strips or prefixes the path unexpectedly
  • The asset was not included in the build or deploy artifact

Diagnosis steps

Compare the requested path with the route table

Interpretation

A missing leading slash, an extra prefix, or a case difference reveals a routing mismatch.

Test the same path against the origin server directly

curl -I http://origin/internal-path

Interpretation

Success at the origin but 404 through the proxy localises the fault to proxy rewriting.

Check the deploy artifact for the file

Interpretation

A 404 on a static asset often means the build output was not uploaded or was ignored by deploy rules.

Root cause

A 404 means no resource matched the requested path. In SPAs the server must return index.html for client-side routes, or direct navigation and refreshes 404. Proxies can also rewrite paths so the upstream sees a different one than the browser requested, and case-sensitive filesystems on Linux differ from case-insensitive ones on macOS or Windows.

Fix

Configure the SPA fallback so unmatched paths serve index.html while API and asset routes remain exact. Normalise trailing slashes and case, and make route matching explicit. Align proxy path rewriting with the application expectations, and verify the deploy artifact includes every referenced asset.

Verify the fix

Request the previously 404 path directly and via refresh; both return the expected content, and origin logs show the request reaching the intended route.

Symptoms

A path that exists in the app returns 404, often only in production. Client-side routes frequently 404 on refresh because the server has no matching file.

Diagnosis

Compare and test at the origin:

curl -I https://app.example.com/settings   # through proxy
curl -I http://127.0.0.1:3000/settings     # origin

If the origin succeeds and the proxy 404s, the proxy path handling is at fault.

Fix

Add an SPA fallback that preserves API routes:

location / {
  try_files $uri /index.html;
}
location /api/ {
  proxy_pass http://backend;
}

Normalise trailing slashes and match case exactly.

Verification

Request the failing path directly and after a refresh; both return the page, and origin access logs show the intended route handling the request.

SPA Fallback Caveats

A blanket fallback that serves index.html for every unmatched path works for client routes but quietly breaks two other cases. First, it can mask genuine API 404s, returning HTML where a client expects JSON, which turns a clear error into a confusing parse failure. Second, missing static assets start returning the HTML page with a 200, so a stale script reference looks successful while delivering the wrong content type. Scope the fallback so HTML routes are served but API prefixes and asset extensions are excluded, and keep returning a true 404 for anything the app does not own.