Redirects

Send Clients to a Different URL With ctx.redirect()

Redirects

Use `ctx.redirect()` to issue 301 or 302 redirects, redirect back to the referrer, and generate safe redirect URLs from named routes with `router.url()`.

3 min read Level 2/5 #koa#routing#redirect
What you'll learn
  • Issue a redirect with `ctx.redirect()`
  • Choose between 301 permanent and 302 temporary redirects
  • Generate route URLs with `router.url()` for safe redirects

A redirect tells the client to fetch a different URL. Koa makes this simple with ctx.redirect(). The default status is 302 Found (temporary); change it to 301 for permanent moves.

Basic Redirect

router.get("/old-path", async (ctx) => {
  ctx.redirect("/new-path");
  // status is 302 by default
});

ctx.redirect() sets the Location header and writes a short HTML body so browsers that don’t follow redirects automatically show a link.

301 Permanent Redirect

Set ctx.status before calling ctx.redirect():

router.get("/blog", async (ctx) => {
  ctx.status = 301;
  ctx.redirect("/posts");
});

Use 301 only when the old URL will never come back — search engines transfer link equity and browsers cache it permanently.

302 Temporary Redirect

302 is the safe default. Use it when the destination may change (login redirects, A/B tests, maintenance pages):

router.get("/dashboard", async (ctx) => {
  if (!ctx.session?.user) {
    ctx.redirect("/login"); // 302
  } else {
    ctx.body = "welcome";
  }
});

Redirect Back to Referrer

"back" redirects to the Referer header, with a fallback:

router.post("/logout", async (ctx) => {
  ctx.session = null;
  ctx.redirect("back", "/");
  // goes to Referer, or "/" if header is absent
});

Generating URLs With router.url()

Hard-coding paths in redirects breaks when you rename a route. Name your routes and use router.url() to generate the URL:

router.get("user-detail", "/users/:id", async (ctx) => {
  ctx.body = { id: ctx.params.id };
});

router.post("/users", async (ctx) => {
  const id = "42"; // newly created user ID
  ctx.status = 201;
  ctx.redirect(router.url("user-detail", { id }));
  // redirects to /users/42
});

The first argument to router.get() (before the path) is the route name. router.url(name, params) fills in the placeholders.

External Redirects

Pass a full URL to redirect outside your app:

router.get("/docs", async (ctx) => {
  ctx.redirect("https://koajs.com");
});

Status Code Reference

CodeNameWhen to use
301Moved PermanentlyURL will never return
302Found (temporary)Temporary or conditional redirect
303See OtherAfter POST — redirect to GET result
307Temporary RedirectPreserve HTTP method on redirect
308Permanent RedirectPreserve HTTP method, permanent

Up Next

When no route matches at all, Koa needs a catch-all to return a proper 404 instead of an empty response.

404 Handling →