Rewrites vs Redirects in Next.js: The Real Difference

A redirect sends the browser to a new URL and changes the address bar. A rewrite serves different content under the same URL. Here is how each behaves in Next.js and when to pick one.

7 min read

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.

typescripttypescript
// 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 nextConfig

Requesting /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.

Redirect and rewrite compared

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.

BehaviorRedirectRewrite
URL in the address barChanges to the destinationStays as requested
Round tripsTwoOne
Status code307 or 308200 from the destination
Destination visible to the browserYesNo
Can target an external siteYes, the user leavesYes, the content is proxied
Order in the routing pipelineBefore ProxyAfter 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.

typescripttypescript
// 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.

typescripttypescript
// 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.

Do not use a rewrite to keep two URLs alive

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

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.
RunePowered by Rune AI

Frequently Asked Questions

Does a rewrite change the URL in the address bar?

No. A rewrite is resolved on the server, so the browser keeps the URL it asked for and never learns about the destination path. Only a redirect changes the address bar.

Which status code does a Next.js redirect use?

Config redirects return 308 when permanent is true and 307 when it is false. Next.js prefers these over 301 and 302 because 307 and 308 preserve the original request method instead of turning a POST into a GET.

Can a rewrite point at a different domain?

Yes. A rewrite destination can be an absolute external URL, which makes the other site's response appear under your own path. That is the basis of incremental migration and of proxying an external API through your app.

Do rewrites apply during client-side navigation?

Yes. Config rewrites are applied to client-side routing too, so a Link to a rewritten path serves the destination content while the displayed URL stays the same.

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.