Fix: Express Cannot GET /route (404 Not Found)
Quick Answer
How to fix Express.js Cannot GET route 404 error caused by wrong route paths, missing middleware, route order issues, static files, and router mounting problems.
The Error
You visit a route in your Express app and get:
Cannot GET /api/usersOr:
<!DOCTYPE html>
<html>
<head><title>Error</title></head>
<body><pre>Cannot GET /api/users</pre></body>
</html>With HTTP status 404 Not Found.
Or for POST requests:
Cannot POST /api/usersExpress does not have a route handler registered for the requested URL and HTTP method. The request falls through all route handlers and Express responds with its default 404 message.
Why This Happens
Express matches incoming requests against registered routes in order. If no route matches the URL and HTTP method, Express returns “Cannot GET /path” (or “Cannot POST”, etc.).
Common causes:
- Route path typo.
/api/userregistered but requesting/api/users. - Wrong HTTP method. Route uses
app.post()but the request is a GET. - Route order issue. A catch-all route or middleware above intercepts the request.
- Router not mounted. A router module is defined but never
app.use()’d. - Missing leading slash.
api/usersinstead of/api/users. - Case sensitivity. Express routes are case-insensitive by default, but path params are not.
- Trailing slash mismatch.
/api/users/vs/api/users.
Fix 1: Check the Route Path
Verify the registered route matches exactly:
Broken — typo in route:
// Registered: /api/user (singular)
app.get('/api/user', (req, res) => {
res.json({ users: [] });
});
// Request: /api/users (plural) → Cannot GET /api/usersFixed:
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});Debug — list all registered routes:
// Print all routes after setup
app._router.stack.forEach((r) => {
if (r.route) {
console.log(`${Object.keys(r.route.methods).join(',')} ${r.route.path}`);
}
});Or use express-list-endpoints:
npm install express-list-endpointsconst listEndpoints = require('express-list-endpoints');
console.log(listEndpoints(app));Pro Tip: When you get “Cannot GET”, first check the exact URL you are requesting against the exact routes registered. A single character difference (trailing slash, plural vs singular, typo) causes a 404.
Fix 2: Fix Route Method Mismatch
The HTTP method must match:
// Only responds to POST
app.post('/api/users', (req, res) => {
res.json({ created: true });
});
// GET /api/users → Cannot GET /api/users
// POST /api/users → { created: true }Handle multiple methods:
app.route('/api/users')
.get((req, res) => {
res.json({ users: [] });
})
.post((req, res) => {
res.json({ created: true });
});Or use app.all() for any method:
app.all('/api/health', (req, res) => {
res.json({ status: 'ok' });
});Fix 3: Fix Router Mounting
If you define routes in a separate router file, you must mount it:
Broken — router defined but not mounted:
// routes/users.js
const router = require('express').Router();
router.get('/', (req, res) => {
res.json({ users: [] });
});
module.exports = router;
// app.js
const userRoutes = require('./routes/users');
// Forgot to mount! userRoutes is imported but never usedFixed — mount the router:
// app.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes); // Mount at /api/usersRoute prefix matters:
// routes/users.js
router.get('/', ...); // Matches /api/users
router.get('/:id', ...); // Matches /api/users/123
router.post('/', ...); // Matches POST /api/users
// app.js
app.use('/api/users', userRoutes); // Prefix: /api/usersThe router’s paths are relative to the mount point. router.get('/') + app.use('/api/users', router) = /api/users.
Common Mistake: Defining the full path in both the router and the mount point.
router.get('/api/users')mounted atapp.use('/api', router)creates/api/api/users, not/api/users.
Fix 4: Fix Route Order
Express matches routes in the order they are defined. Earlier matches win:
Broken — catch-all before specific route:
// This catches everything!
app.get('*', (req, res) => {
res.sendFile('index.html');
});
// This never runs
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});Fixed — specific routes before catch-all:
// API routes first
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});
// Catch-all for SPA routing last
app.get('*', (req, res) => {
res.sendFile('index.html');
});Broken — static files middleware before routes:
app.use(express.static('public'));
// If public/api/users/index.html exists, it will be served instead of the route
app.get('/api/users', handler);Fixed — mount API routes before static files:
app.use('/api', apiRouter);
app.use(express.static('public'));Fix 5: Fix Static File Serving
If you are trying to serve static files (HTML, CSS, JS):
const path = require('path');
// Serve files from the 'public' directory
app.use(express.static(path.join(__dirname, 'public')));
// File at public/index.html → http://localhost:3000/index.html
// File at public/css/style.css → http://localhost:3000/css/style.cssFor SPA (React, Vue, Angular) with client-side routing:
// API routes
app.use('/api', apiRouter);
// Static files
app.use(express.static(path.join(__dirname, 'build')));
// SPA fallback — must be LAST
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});Fix 6: Fix Trailing Slash Issues
/api/users and /api/users/ are different routes by default:
app.get('/api/users', handler);
// GET /api/users → 200
// GET /api/users/ → 301 redirect to /api/users (default Express behavior)Enable strict routing to treat them as different:
app.set('strict routing', true);
// Now /api/users and /api/users/ are different routesOr handle both explicitly:
app.get('/api/users/?', handler); // Regex: trailing slash optionalFix 7: Fix Body Parsing Middleware
Missing body parser causes POST/PUT handlers to fail silently:
// Must be before route definitions!
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse form bodies
// Now POST routes can access req.body
app.post('/api/users', (req, res) => {
console.log(req.body); // Without express.json(), this is undefined
res.json({ created: true });
});Fix 8: Add a Custom 404 Handler
Replace Express’s default “Cannot GET” response:
// Define all routes first...
// 404 handler — must be after all routes
app.use((req, res, next) => {
res.status(404).json({
error: 'Not Found',
message: `Route ${req.method} ${req.url} not found`,
timestamp: new Date().toISOString(),
});
});
// Error handler — must be after 404 handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Internal Server Error',
message: err.message,
});
});Still Not Working?
Check that the server is running on the expected port:
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});Check for proxy issues. If using Nginx or a reverse proxy, the proxy path might not match:
# Nginx strips /api prefix if not configured correctly
location /api/ {
proxy_pass http://localhost:3000/api/; # Include trailing slash on both
}Check for CORS preflight failures. OPTIONS requests return 404 if not handled:
const cors = require('cors');
app.use(cors()); // Handles OPTIONS preflight requestsFor Node.js module errors, see Fix: Node.js ERR_MODULE_NOT_FOUND. For port already in use, see Fix: Port 3000 already in use. For CORS errors, see Fix: CORS Access-Control-Allow-Origin error.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
How to fix the JavaScript heap out of memory error by increasing Node.js memory limits, fixing memory leaks, and optimizing builds in webpack, Vite, and Docker.
Fix: Error: error:0308010C:digital envelope routines::unsupported
How to fix the Node.js digital envelope routines unsupported error caused by OpenSSL 3.0 changes in Node.js 17+, with solutions for webpack, Vite, React, and Angular.
Fix: React Warning: Failed prop type
How to fix the React 'Warning: Failed prop type' error. Covers wrong prop types, missing required props, children type issues, shape and oneOf PropTypes, migrating to TypeScript, default props, and third-party component mismatches.
Fix: SyntaxError: Cannot use import statement outside a module
How to fix 'SyntaxError: Cannot use import statement outside a module' in Node.js, TypeScript, Jest, and browsers by configuring ESM, package.json type, and transpiler settings.