Chaining Express.js 4's res.status(401) to a redirect Chaining Express.js 4's res.status(401) to a redirect express express

Chaining Express.js 4's res.status(401) to a redirect


There are some subtle diferences with the methods for sending back a new location header.

With redirect:

app.get('/foobar', function (req, res) {  res.redirect(401, '/foo');});// Responds withHTTP/1.1 401 UnauthorizedX-Powered-By: ExpressLocation: /fooVary: AcceptContent-Type: text/plain; charset=utf-8Content-Length: 33Date: Tue, 07 Apr 2015 01:25:17 GMTConnection: keep-aliveUnauthorized. Redirecting to /foo

With status and location:

app.get('/foobar', function (req, res) {  res.status(401).location('/foo').end();});// Responds withHTTP/1.1 401 UnauthorizedX-Powered-By: ExpressLocation: /fooDate: Tue, 07 Apr 2015 01:30:45 GMTConnection: keep-aliveTransfer-Encoding: chunked

With the original (incorrect) approach using redirect:

app.get('/foobar', function (req, res) {  res.status(401).redirect('/foo')();});// Responds with HTTP/1.1 302 Moved TemporarilyX-Powered-By: ExpressLocation: /fooVary: AcceptContent-Type: text/plain; charset=utf-8Content-Length: 38Date: Tue, 07 Apr 2015 01:26:38 GMTConnection: keep-aliveMoved Temporarily. Redirecting to /foo

So it looks like redirect will abandon any previous status codes and send the default value (unless specified inside the method call). This makes sense due to the use of middleware within Express. If you had some global middleware doing pre-checks on all requests (like checking for the correct accepts headers, etc.) they wouldn't know to redirect a request. However the authentication middleware would and thus it would know to override any previous settings to set them correctly.

UPDATE: As stated in the comments below that even though Express can send a 4XX status code with a Location header does not mean it is an acceptable response for a request client to understand according to the specs. In fact most will ignore the Location header unless the status code is a 3XX value.


You can certainly send a Location: /login header alongside with your 401 page, however, this is ill-advised, and most browsers will not follow it, as per rfc2616.

One way to do overcome this, is to serve <meta http-equiv="refresh" content="0; url=/login"> alongside with your 401 page:

res.set('Content-Type', 'text/html');res.status(401).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/login"></head></html>`);


I fell on the same issue and decided to use the session to handle this kind of job.

I didn't want to have an intermediate view...

With the below code, I can redirect to the homepage, which will be rendered with 401 unauthorized code.

app.get('patternForbiddenRoute', (req, res, next) => {       // previousCode       if (notForbidden === true) {           return res.render("a_view");       }       req.session.httpCode = 401;       res.redirect('patternHomeRoute');});app.get('patternHomeRoute', (req, res, next) => {       res.render("my_home_view", {}, (error, html) => {            // ... handle error code ...            const httpCode = req.session.httpCode;            if (httpCode !== undefined) {                delete req.session.httpCode;                res.status(httpCode);            }            res.send(html);       }));});