Search code examples
javascripthttp-redirectkoa2

KoaJS ctx.redirect() causing ERR_TOO_MANY_REDIRECTS in Chrome


I'm new to KoaJS. Playing a bit now. I'm trying to redirect all request to a particular URL using a middle-ware. This seems to product ERR_TOO_MANY_REDIRECTS in Chrome. I tried a lot to debug. Can't get what is wrong.

index.js

// App
const Koa = require('koa')
const app = new Koa()

// Parser
const bodyParser = require('koa-body')
app.use(bodyParser())

// Session
const session = require('koa-session')
app.keys = ['asdfasdf@#$ASDf1#$@5rasdf']
app.use(session(app))
// THIS MIDDLEWARE
app.use(async (ctx, next) => {
    ctx.session.user = '121' // This is all playground. No production stuff.
    const s = ctx.session.user
    if (s != '1213') {
        ctx.redirect('/login')
    }
    await next()
})

// Router
const common = require('./routes')
app.use(common.routes())

// Server
app.listen(3000, () => { console.log('Listening on http://localhost:3000') })

routes.js

const Router = require('koa-router')
const router = new Router()

// const User = require('./user')

router.get('/', async ctx => {
    ctx.body = 'Home Page'
})

router.get('/login', async ctx => {
    ctx.body = 'Login Page'
})

module.exports = router

Solution

  • Consider your middleware:

    app.use(async (ctx, next) => {
        ctx.session.user = '121' // This is all playground. No production stuff.
        const s = ctx.session.user
        if (s != '1213') {
            ctx.redirect('/login')
        }
        await next()
    })
    

    Because s != '1213' always evaluates to "true", ctx.redirect('/login') is executed for every request.

    This will do two things:

    • set the HTTP response code to 302, telling the browser to perform a redirect
    • set the Location header to /login, telling the browser to location to redirect to

    Considering that this happens for every request, you end up in a loop: a request to / is redirected to /login, which itself is redirected to /login, which is also redirected to /login, ad infinitum. At some point, the browser gives up and issues a ERR_TOO_MANY_REDIRECTS error.

    FWIW, after calling ctx.redirect(), you typically end the request, for instance like this:

    if (s != '1213') {
        return ctx.redirect('/login')
    }
    

    In your case, you don't end the request, which means that it will be passed to the router.

    To answer your comment, I assume you used this:

    if (s != '1213') {
        ctx.url = '/login';
    }
    

    You change the URL that the router will check to see which handler it should call. Sort of like an internal redirect, or a "rewrite": a request to / is handled internally as if it were a request for /login.

    This is not something that you want though, because it may confuse the browser. The correct way is to issue a proper redirect, using ctx.redirect(), which will make the browser change the URL in the location bar and issue a new request.