HTTP 404 Not Found — What It Means and How to Fix It
404 Not Found
Verified against RFC 9110 §15.5.5 (HTTP Semantics), MDN Web Docs: 404 Not Found, Google Search Central: soft 404 errors · Updated June 2026
> quick_fix
The server found no resource at the given URL. Verify the URL is correct, the route exists on the server, and the resource hasn't been deleted or moved. For SPAs, ensure your server returns the index.html for all routes instead of letting the file system handle them.
# Confirm the exact URL being requested
curl -si https://api.example.com/v1/users/123 | head -5
# Check if trailing slash matters
curl -si https://api.example.com/api/users # vs
curl -si https://api.example.com/api/users/What causes this error
HTTP 404 means the server has no handler or resource mapped to the requested URL. Common causes: URL typo or version mismatch (`/v1/` vs `/v2/`), resource genuinely deleted or moved, trailing slash mismatch, SPA routes returning 404 on hard refresh because the server doesn't know about client-side routes, case-sensitive paths on Linux servers, and API versioning migration that left old endpoints without redirects.
How to fix it
- 01
step 1
Double-check the exact URL including version, trailing slash, and case
Linux web servers are case-sensitive. `/Users/123` and `/users/123` are different paths. Also check for a trailing slash difference — `/api/users` vs `/api/users/` behaves differently depending on server config. Get the precise URL from your API docs.
# Test with and without trailing slash curl -si https://api.example.com/users -o /dev/null -w '%{http_code}\n' curl -si https://api.example.com/users/ -o /dev/null -w '%{http_code}\n' # Follow redirects to see where you end up curl -siL https://api.example.com/users - 02
step 2
For SPAs: configure the server to return index.html for all routes
React, Vue, and Angular apps handle routing client-side. When a user navigates to `/dashboard/settings` directly, the server gets a request for that path and returns 404 because there's no file there. The fix: configure the server to return `index.html` for all non-file paths.
# Nginx — serve index.html for all SPA routes location / { try_files $uri $uri/ /index.html; } # Vercel — vercel.json { "rewrites": [{"source": "/(.*)", "destination": "/index.html"}] } - 03
step 3
For Next.js/Express: verify the route handler exists
Print all registered routes to confirm the route exists on the server side. In Express, use `express-list-routes`. In Next.js, check the `app/` or `pages/` directory structure.
// Express: list all routes at startup const listRoutes = require('express-list-routes') listRoutes(app) // prints all registered routes // Or manually inspect router stack app._router.stack.forEach(r => { if (r.route) console.log(r.route.path) }) - 04
step 4
Check if the resource was moved — add redirects
If a URL changed due to a refactor or API versioning, add 301 (permanent) or 302 (temporary) redirects from the old path to the new one. Never leave old URLs returning 404 — it breaks clients and loses SEO equity.
// Express: add a redirect from old to new path app.get('/api/v1/users/:id', (req, res) => { res.redirect(301, `/api/v2/users/${req.params.id}`) }) - 05
step 5
Distinguish intentional 404 from security-motivated 404
Some APIs return 404 instead of 403 when a resource exists but the requester can't access it (to avoid disclosing resource existence). If you suspect this, try authenticating and check if the response code changes. AWS S3 and Stripe both use this pattern.
# Test without auth vs with auth — if 404 becomes 403 or 200, it's an access issue curl -si https://api.example.com/resource/123 curl -si https://api.example.com/resource/123 \ -H 'Authorization: Bearer token'
How to verify the fix
- The URL returns a 2xx response.
- Old URLs that were moved return a 301 redirect to the new location.
- SPA routes work on hard refresh (not just initial navigation).
Why 404 happens at the runtime level
HTTP 404 is generated when the server's URL routing table has no match for the requested path and method combination. In file servers, this means no file exists at the mapped filesystem path. In application servers, it means no route handler matches the URL pattern. Next.js App Router generates 404 at build time for statically generated pages with no matching `generateStaticParams` entry, and at request time when a dynamic route handler returns `notFound()`. Express generates 404 by falling through all registered middleware without a response, typically caught by a final catch-all handler.
Common debug mistakes for 404
- SPA deployed without configuring the server to serve `index.html` for all client-side routes — works on initial load, fails on hard refresh.
- API versioning upgrade where old `/v1/` endpoints were removed without 301 redirects, breaking all existing clients.
- Trailing slash mismatch — server configured with `strict: true` in Express treats `/users` and `/users/` as different routes.
- Case sensitivity: deploying to Linux after developing on macOS (case-insensitive) — `/Users` works locally but 404s in production.
- Dynamic route parameter mismatch: Next.js `[id].tsx` doesn't match a URL with multiple segments like `/users/123/profile`.
When 404 signals a deeper problem
A high 404 rate in Google Search Console signals broken internal linking, missing redirect infrastructure, or content that was deleted without being replaced. Every 404 that was previously indexed costs crawl budget and dilutes domain authority. The architectural fix for content-heavy sites is a redirect management system — a database-driven redirect layer where editors can register redirects without code deployments, paired with a 404 monitoring dashboard that shows which previously-indexed URLs are now returning 404 so they can be redirected proactively.
Editor's take
The 404 looks trivial but becomes a production incident in two scenarios. First: a mobile app hardcodes API endpoints and a backend team removes a deprecated `/v1/` route without coordinating a versioned deprecation window. Users who haven't updated the app suddenly can't load any data. The right solution is a long deprecation period with `Sunset` headers, not a hard cut. Second: an e-commerce redirect chain breaks — product URLs changed in a replatform, the redirect table was partially imported, and 15% of Google-indexed product pages now 404, destroying organic traffic overnight.
The SPA 404-on-refresh problem is a classic first deployment mistake. The developer tested every route by clicking through the app, never by typing URLs into the address bar or refreshing. In production, the CDN or reverse proxy sees `/dashboard/settings`, finds no file at that path, and serves a 404 before the JavaScript app ever loads. The fix is two lines of nginx config or a single `vercel.json` rewrite rule, but it requires knowing that the problem exists — which requires someone to actually test hard refresh on every route before declaring a deploy done.
The adjacent error worth knowing: 308 (Permanent Redirect) vs 301 for POST requests. When you redirect a form submission endpoint, a 301 changes the method to GET (breaking the form). A 308 preserves the method. Most developers don't know 308 exists and spend time debugging why their redirected POST endpoint returns an empty body.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
Is a 404 cached by browsers?
By default, browsers cache 404 responses according to the `Cache-Control` header. If your server sends `Cache-Control: max-age=3600` on 404 responses, browsers cache the 404 for an hour. Add `Cache-Control: no-store` to your 404 responses to prevent stale caching, especially on resources that might be created later.
What is a soft 404?
A soft 404 returns a 200 HTTP status code but displays a 'not found' page to the user. This confuses search engines — Googlebot thinks the page is valid content and may index an empty or generic 'page not found' page. Always return the actual 404 status code from the server when content doesn't exist, even if the custom error page is styled.
Should I use 404 or 410 for deleted content?
Use 410 (Gone) when a resource was intentionally deleted and won't come back. It's a signal to crawlers to permanently remove it from their index, faster than a 404. Use 404 when you're unsure whether the resource will return. In practice, most developers use 404 for both, which is acceptable.