Send the Browser Elsewhere With @Redirect
Redirects
Static and dynamic HTTP redirects with the `@Redirect` decorator.
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
| Code | Meaning | Method preserved? |
|---|---|---|
| 301 | Moved permanently | No (becomes GET) |
| 302 | Found / temporary | No (becomes GET) |
| 303 | See other | No (becomes GET) |
| 307 | Temporary, preserve | Yes |
| 308 | Permanent, preserve | Yes |
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('//');
}