Last updated: Apr 5, 2024
Reading timeยท4 min

The "Cannot POST /" error in Express and Node.js is caused when there is a mismatch between the path in the route handler and the path your form is making a POST request to.
To solve the error, make sure the action attribute of the form corresponds
to the path in the request handler.

Here is a minimal example of making a POST request in an Express.js application.
Let's start with the form that makes the POST request.
This is my index.html file.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <h2>bobbyhadz.com</h2> <form action="/register" method="post"> <div> <label>Username:</label> <input type="text" name="username" /><br /> </div> <div> <label>Password:</label> <input type="password" name="password" /> </div> <div><input type="submit" value="Register" /></div> </form> </body> </html>
The form is quite minimal, it has 2 fields - a username and a password.
This is my index.js file.
// ๐๏ธ using require() CommonJS imports const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), ); app.get('/register', function (req, res) { // ๐๏ธ if your HTML file is in the root directory (next to package.json) res.sendFile(__dirname + '/index.html'); }); app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); }); const port = 5000; app.listen(port, () => { console.log(`Example app listening on port ${port}`); });
Here is the equivalent code sample using the ES6 modules import/export syntax.
// ๐๏ธ using ES6 Modules import/export syntax import express from 'express'; import bodyParser from 'body-parser'; import path from 'path'; import {fileURLToPath} from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), ); app.get('/register', function (req, res) { // ๐๏ธ if your HTML file is in the root directory (next to package.json) res.sendFile(__dirname + '/index.html'); }); app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); }); const port = 5000; app.listen(port, () => { console.log(`Example app listening on port ${port}`); });
You can start the server with npx nodemon index.js and open
http://localhost:5000/register.
npx nodemon index.js

As shown in the short clip everything works as expected.
Things to note when solving the error:
action attribute matches the path in your
app.post route handler.For example, my form element has an action attribute set to /register.
<form action="/register" method="post">
So my route handler should also set its path to /register.
app.post('/register', function (req, res) { // ... })
You should also have a corresponding app.get handler that is used to render
the form when the user visits the page.
app.get('/register', function (req, res) { // ๐๏ธ if your HTML file is in the root directory (next to package.json) res.sendFile(__dirname + '/index.html'); });
/register route, the app.get() route handler
renders the index.html file (the form).action
attribute POSTS the data to the /register route.app.post() route handler for /register.app.post() route handler logs the submitted username and password and
sends them back to the user.app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); });
The code sample assumes that your index.html and index.js files are located
in the root directory of your project (where your package.json file is).

bodyParser
module.// ๐๏ธ using require() CommonJS syntax const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), );
And here is the equivalent code sample using the ES6 modules import/export syntax.
// ๐๏ธ using ES6 import/export syntax import express from 'express'; import bodyParser from 'body-parser'; import path from 'path'; import {fileURLToPath} from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), );
The code sample assumes that you have a /public directory where you store your
static (JS, CSS, images) files.
req.body object.app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); });
The action attribute of the form element in your index.html file should
match the path in your app.get() and app.post() request handlers.
<form action="/register" method="post">
The action attribute is set to /register in the example above.
Here is the app.get() route handler which renders the form element when the
user visits /register.
app.get('/register', function (req, res) { res.sendFile(__dirname + '/index.html'); });
The route handler shows the form when the user visits the /register route.
And here is the app.post() route handler which is called when the user submits
the form (because the form's action corresponds to the path).
app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); });
The route gets triggered when the form with action set to /register is
submitted.
You can learn more about the related topics by checking out the following tutorials: