express: '/' route breaks strict routing

Hi there,

Currently, if you define a route like this:

route = require('express').Router({strict: true});
route.get('/', function(req, res) {
  res.send('hi');
});

And use() that in an express application like this:

app = require('express')();
app.use('/strict/', route);

You will receive a 200 when requesting /strict/ and /strict. I would expect only /strict/ to return a 200.

I’ve found an acceptable workaround by adding a check like this:

route.get('/', function(req, res, next) {
    if (req.originalUrl.slice(-1) != '/') return next();
    res.send('root with slash');
});
route.get('/', function(req, res) {
    res.send('root without slash');
});

But I think it would be less surprising if route.get('/') only worked for the path ending in / when strict routing is enabled, and perhaps route.get('') could be used for the no-slash case.

About this issue

  • Original URL
  • State: open
  • Created 10 years ago
  • Reactions: 13
  • Comments: 32 (18 by maintainers)

Commits related to this issue

Most upvoted comments

I use middleware for add / and redirect all routes in new address. I fink it’s possible to use in strict mode.

app.use((req, res, next) => {
  const url = req.url;
  const fileRegExp = new RegExp(/\./);
  const isParams = Object.keys(req.params).length;
  const isQuery = Object.keys(req.query).length;
  if(url.length && fileRegExp.test(url) || isParams || isQuery){
    next();
  } else if(url.length && url.slice(-1) != '/') {
    res.redirect(301, `${url}/`);
  } else {
    next();
  }
});

.From documentation I would expect that my application will not serve “/path” endpoint, yet it gets forwarded to given router.

Unfortunately the documentation in this case is simply wrong. The strict routing feature in no way affects paths under app.use, and it is not intended to. This issue is definitely different from what you are describing.

I am not sure if this requires a separate issue, since, by the looks of it, it will be fixed as soon as express updates it’s dependancy of path-to-regex.

It will not, because app.use is not affected by strict routing, and will always act in a non-strict manor.

There is a lot of off topic chatter in this thread, and some is issues like yours that are actually misunderstandings and some are of the original bug. The bug is only the code in the initial post. It is a bug because router.get is being used, which absolutely should be enforcing strict routing. Your code is not utilizing any methods that actually are affected by strict routing.

If you don’t care about supporting empty paths, using the below middleware seems to enforce strict routing:

//  Results:
//
//  GET /strict -> (404)
//  GET /strict/ -> slash
//  GET /strict// -> (404)
//  GET /strictslash -> (404)
//  GET /strictslash/ -> slash
//  GET /strictslash// -> (404)
//  GET /strictslash/// -> (404)

const express = require("express");

const app = express();
app.set("strict routing", true);

const router = express.Router({ strict: true });

//  Middleware to enforce strict URLs
//  Note: must be applied to the router (and not app)
router.use(function strict(req, res, next) {
  if (req.app.get("strict routing")) {
    req.url = req.originalUrl.substring(req.baseUrl.length);
  }
  next();
});

//  This solution does not support empty paths like these:
//  router.use("", function (req, res, next) {
router.all("", function (req, res, next) {
  res.send("noslash");
});

//  Note: using .use() will not work, use methods instead (all, get, post, etc):
//  router.use("", function (req, res, next) {
router.all("/", function(req, res) {
    res.send("slash");
});

app.use("/strict", router);
app.use("/strictslash/", router);

app.listen(4000);

@danieljuhl the issue is really a fundamental functionality of API guarantees within the router. The only work-around right now is to make both routers strict (your app with strict routing and the router strict) or use the work-around posted in the initial issue comment.

Another related case…

var express = require('express');
var app = express();
var router = express.Router({ strict: true });

router.use('', function (req, res) {
  res.send('hi');
});

app.use('/strict', router);
app.listen(4000);

GET /strict should give 200 and GET /strict/ should give 404, but both give a 200. Or is there another way to route a path to the routers root?