Redirects

Send the Browser Elsewhere With @Redirect

Redirects

Static and dynamic HTTP redirects with the `@Redirect` decorator.

4 min read Level 1/5 #nestjs#http#redirect
What you'll learn
  • Use `@Redirect` for static redirects
  • Return `{ url, statusCode }` for dynamic redirects
  • Pick the right code — 301, 302, 307, or 308

Sometimes a route shouldn’t return a payload at all — it should send the client somewhere else. Nest’s @Redirect decorator handles both static and dynamic cases without dropping into raw response calls.

Static Redirect

import { Controller, Get, Redirect } from '@nestjs/common';

@Controller()
export class AppController {
  @Get('docs')
  @Redirect('https://docs.example.com', 302)
  goDocs() {}
}

A GET /docs request returns a 302 Found with a Location header. The method body can stay empty — the decorator does the work.

Dynamic Redirect

Return an object with url and (optionally) statusCode to override:

@Get('out')
@Redirect()
out(@Query('to') to: string) {
  if (!isSafe(to)) return { url: '/', statusCode: 302 };
  return { url: to, statusCode: 302 };
}

The decorator’s defaults (no URL, status 302) get overridden by whatever you return. Pass @Redirect('fallback') to set a static default that the handler can replace.

Choose the Right Status

CodeMeaningMethod preserved?
301Moved permanentlyNo (becomes GET)
302Found / temporaryNo (becomes GET)
303See otherNo (becomes GET)
307Temporary, preserveYes
308Permanent, preserveYes

The “preserve” column matters when you’re redirecting a POST. With 302 the browser turns it into a GET to the new URL; with 307/308 it re-issues the POST. For most app-style redirects (after login, after form submit), use 303 to land on a clean GET.

Why Not Just Set the Header Yourself?

You can: @Res({ passthrough: true }) + res.redirect(url) works fine. But @Redirect keeps the controller declarative — the route’s intent is visible at a glance, and it’s trivial to test (just check the return value).

Open-Redirect Trap

If your redirect target comes from user input (?to=...), validate it against an allowlist or restrict it to relative paths. Open redirects are a classic phishing vector — your app vouches for a URL it shouldn’t.

function isSafe(to: string) {
  return to.startsWith('/') && !to.startsWith('//');
}
Async Handlers & Observables →