principletypescriptnextjsModerate
redirect() vs rewrite in Next.js: navigation vs URL masking
Viewed 0 times
Next.js 9.5+ for config redirects/rewrites; redirect() from next/navigation in App Router
redirectrewritepermanentRedirectURL masking301 redirectnext.config redirects
Error Messages
Problem
Developers confuse redirect() (changes the browser URL) with rewrites (serves different content under the same URL) and use them interchangeably, causing SEO issues or broken user experiences.
Solution
Use redirect() for user navigation, rewrites for URL masking:
// redirect() — changes browser URL, for auth gates and moved pages
import { redirect } from 'next/navigation';
export default async function Page() {
const session = await getSession();
if (!session) redirect('/login');
}
// permanentRedirect() — for SEO-safe permanent moves (308)
import { permanentRedirect } from 'next/navigation';
permanentRedirect('/new-url');
// next.config.js redirects — run at the edge before rendering
module.exports = {
async redirects() {
return [
{ source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true },
];
},
async rewrites() {
return [
// URL stays /dashboard, but Next.js serves /app/dashboard
{ source: '/dashboard', destination: '/app/dashboard' },
// Proxy to external API without exposing the URL
{ source: '/api/external/:path', destination: 'https://api.third-party.com/:path' },
];
},
};
// redirect() — changes browser URL, for auth gates and moved pages
import { redirect } from 'next/navigation';
export default async function Page() {
const session = await getSession();
if (!session) redirect('/login');
}
// permanentRedirect() — for SEO-safe permanent moves (308)
import { permanentRedirect } from 'next/navigation';
permanentRedirect('/new-url');
// next.config.js redirects — run at the edge before rendering
module.exports = {
async redirects() {
return [
{ source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true },
];
},
async rewrites() {
return [
// URL stays /dashboard, but Next.js serves /app/dashboard
{ source: '/dashboard', destination: '/app/dashboard' },
// Proxy to external API without exposing the URL
{ source: '/api/external/:path', destination: 'https://api.third-party.com/:path' },
];
},
};
Why
Redirects tell the browser (and search engines) that a URL has moved — the browser URL changes. Rewrites serve different content under the same URL, useful for clean URLs, A/B testing, or proxying external services. SEO bots see the rewrite destination but users see the source URL.
Gotchas
- redirect() in Server Components throws and terminates — you don't need a return statement after it
- redirect() in Route Handlers must be called outside a try/catch — it throws a special error internally
- permanent: true in next.config.js redirects sends 308 (for POST) or 301 — cached by browsers permanently
- Rewrites run BEFORE middleware in the request lifecycle
Code Snippets
Redirects and rewrites in next.config.js
// next.config.js: permanent redirect for moved page
async redirects() {
return [{ source: '/about-us', destination: '/about', permanent: true }];
},
// rewrite to proxy without exposing backend URL
async rewrites() {
return [{ source: '/api/data/:path*', destination: 'https://internal.svc/:path*' }];
}Context
When handling navigation flows, legacy URL migration, and URL masking in Next.js
Revisions (0)
No revisions yet.