Rewrites vs redirects comes down to one question: does the browser find out about the destination? A redirect answers the request with a new address and the browser asks again, so the URL in the address bar changes. A rewrite resolves the destination on the server and answers immediately, so the URL never changes.
The difference is not in how you declare them. Both are functions in the Next.js config file and both use the same path pattern syntax. The behavior they produce is what differs.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
async redirects() {
return [{ source: '/team', destination: '/about', permanent: true }]
},
async rewrites() {
return [{ source: '/docs', destination: '/documentation' }]
},
}
export default nextConfigRequesting /team lands on /about with that path visible in the address bar. Requesting /docs shows the content of /documentation while the address bar still reads /docs.
The difference in one request cycle
A redirect is two round trips. The server answers the first request with a status code and a location header, the browser reads it and issues a second request, and only that second response contains a page.
A rewrite is one round trip. The server maps the path internally and returns the destination's content in the first response, so the browser never learns that another path was involved.
The top half is the redirect and the bottom half is the rewrite. That extra round trip is the honest cost of a redirect, and the hidden destination is the honest cost of a rewrite.
Both costs are usually worth paying, but they point at different problems. One announces a move, the other conceals a source.
What each one does, side by side
The table below is the short version of everything that actually differs. Path parameters, wildcards, and header or cookie conditions work identically for rewrites and redirects.
| Behavior | Redirect | Rewrite |
|---|---|---|
| URL in the address bar | Changes to the destination | Stays as requested |
| Round trips | Two | One |
| Status code | 307 or 308 | 200 from the destination |
| Destination visible to the browser | Yes | No |
| Can target an external site | Yes, the user leaves | Yes, the content is proxied |
| Order in the routing pipeline | Before Proxy | After Proxy |
That last row matters more than it looks. Config redirects are applied before the proxy file runs, while config rewrites are applied after it, so a redirect can prevent Proxy from ever seeing the request.
The status codes deserve a note too. Next.js uses 307 and 308 rather than 302 and 301 because the newer pair preserves the request method, so a POST stays a POST instead of being silently downgraded to a GET.
When the URL truly moved
Use a redirect when the old path should stop serving content. A renamed section, a retired marketing page, or a consolidated pair of pages all fit, and the permanent flag tells search engines to move their index entry.
// next.config.ts
async redirects() {
return [
{ source: '/blog/:slug', destination: '/news/:slug', permanent: true },
]
}A request to /blog/hiring answers with a 308 and a location header pointing at /news/hiring, and the browser follows it. Any query string on the original request is carried over to the destination automatically.
Set the permanent flag to false while you are still deciding. A 307 is not cached by browsers and search engines the way a 308 is, which means a mistake is easy to undo. Configuring redirects covers the matching options in full.
When the URL should stay
Use a rewrite when the address is the contract and the content behind it is free to move. Serving a section from another service, keeping a legacy path alive after a restructure, or exposing an external API under your own origin all fit.
// next.config.ts
async rewrites() {
return [
{ source: '/help/:path*', destination: 'https://support.example.com/:path*' },
]
}A request to /help/billing returns the support site's response under your own domain. The browser sees one origin, one URL, and one response, which is why this pattern is the backbone of proxying an external API.
Rewrites also apply during client-side navigation. A link to a rewritten path resolves to the destination content while the visible URL stays put, so the behavior is consistent whether the visitor arrives fresh or clicks through.
What each choice does to SEO and analytics
A permanent redirect is a signal. It tells crawlers the old URL is gone and the new one should inherit its ranking, and it removes the old path from the index over time.
A rewrite sends no signal at all. Only the requested URL exists as far as a crawler is concerned, which is what you want when the destination is an implementation detail, and a problem when the destination is genuinely a separate page you want indexed on its own.
Rewriting an old path to a new one leaves both URLs serving identical content, which splits signals between them. Redirect the old path instead, or add a canonical link so crawlers know which URL is authoritative.
Analytics follows the same rule. A redirect shows up as a visit to the destination path, while a rewrite is recorded under the requested path, so a rewritten section will not appear separately in a report unless you tag it yourself.
Which should you use?
The choice between rewrites and redirects has a reliable test. Ask what should happen if someone bookmarks the old address a year from now.
If they should end up somewhere else and see that in the address bar, redirect. If the bookmark should keep working exactly as it did, rewrite.
Two more criteria settle most remaining cases:
- Choose a redirect when the destination is a real page you want people to link to and share.
- Choose a rewrite when the destination is infrastructure, such as another service, an API, or a legacy app you are migrating away from.
When the decision depends on the visitor rather than the path, neither config option is enough. That is the case for redirecting on a cookie or header, which needs the request itself and therefore belongs in the proxy file.
Rune AI
Key Insights
- A redirect changes the URL and costs a second request; a rewrite keeps the URL and costs none.
- Config redirects return 307 or 308, both of which preserve the request method.
- Rewrites mask the destination, including an external one, which is why they suit proxying and migrations.
- Rewrites also apply during client-side navigation, so a Link to a rewritten path resolves the same way.
- Use a redirect when a URL truly moved, and a rewrite when the URL should stay but its content should not.
Frequently Asked Questions
Does a rewrite change the URL in the address bar?
Which status code does a Next.js redirect use?
Can a rewrite point at a different domain?
Do rewrites apply during client-side navigation?
Conclusion
The real difference is who knows about the destination. A redirect tells the browser to ask for a new URL, so the address bar changes and the old path stops serving content. A rewrite keeps the destination private to the server, so one URL keeps serving while the content behind it moves.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.