Redirect all trailing slashes globally in express
I'm adding this answer because I had too many issues with other solutions.
/**
* @param {express.Request} req
* @param {express.Response} res
* @param {express.NextFunction} next
* @return {void}
*/
function checkTrailingSlash(req, res, next) {
const trailingSlashUrl = req.baseUrl + req.url;
if (req.originalUrl !== trailingSlashUrl) {
res.redirect(301, trailingSlashUrl);
} else {
next();
}
}
router.use(checkTrailingSlash);
This will translate:
/page ==> /page/
/page?query=value ==> /page/?query=value
The answer by Tolga Akyüz is inspiring but doesn't work if there is any characters after the slash. For example http://example.com/api/?q=a
is redirected to http://example.com/api
instead of http://example.com/api?q=a
.
Here is an improved version of the proposed middleware that fixes the problem by adding the original query to the end of the redirect destination URL. The version also has a few safety features described in the update notes.
app.use((req, res, next) => {
if (req.path.substr(-1) === '/' && req.path.length > 1) {
const query = req.url.slice(req.path.length)
const safepath = req.path.slice(0, -1).replace(/\/+/g, '/')
res.redirect(301, safepath + query)
} else {
next()
}
})
Update 2016: As noted by jamesk and stated in RFC 1738, the trailing slash can only be omitted when there is nothing after the domain. Therefore, http://example.com?q=a
is an invalid URL where http://example.com/?q=a
is a valid one. In such case, no redirection should be done. Fortunately, the expression req.path.length > 1
takes care of that. For example, given the URL http://example.com/?q=a
, the path req.path
equals to /
and thus the redirection is avoided.
Update 2021: As discovered by Matt, a double slash //
in the beginning of the path causes a dangerous redirection. For example the URL http://example.com//evil.example/
creates a redirect to //evil.example
that is interpreted as http://evil.example
or https://evil.example
by the victim's browser. Also, noted by Van Quyet, there can be multiple trailing slashes that should be handled gracefully. Due to these findings, I added a line that safeguards the path by replacing all consequent slashes by a single /
. I trust the performance overhead caused by the safeguard to be negligible because the regexp literals are only compiled once. Additionally, the code syntax was updated to ES6.
The connect-slashes middleware was designed specifically for this need: https://npmjs.org/package/connect-slashes
Install it with:
$ npm install connect-slashes
Read the full documentation: https://github.com/avinoamr/connect-slashes
Try adding a middleware for that;
app.use((req, res, next) => {
const test = /\?[^]*\//.test(req.url);
if (req.url.substr(-1) === '/' && req.url.length > 1 && !test)
res.redirect(301, req.url.slice(0, -1));
else
next();
});