c
Overview
Understanding Next.js rewrites

Understanding Next.js rewrites

March 1, 2026
4 min read

For many teams, Next.js starts and ends with routing, server-side rendering, and maybe a few API routes. But Next.js is much more than a React framework. It acts as a routing engine, request processor, and architectural foundation for your application. Some of its most impactful capabilities don’t involve components or JSX at all.

One of the most overlooked features? Rewrites.

Let’s break down what rewrites are, how they function, and why they’re architecturally important.

What is a rewrite in Next.js?

A rewrite lets you take an incoming request and serve content from a different internal path — without changing the URL in the browser.

The user asks for one URL. Your app responds from another location. The browser remains unaware of the swap.

This is not the same as a redirect.

Explanation

Redirects instruct the browser to make a new request. Rewrites happen entirely within Next.js, behind the scenes.

A simple rewrite example

Rewrites are configured inside next.config.js:

module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug',
destination: '/content/posts/:slug',
},
]
},
}

If a visitor goes to:

/blog/nextjs-rewrites

Next.js actually serves:

/content/posts/nextjs-rewrites

The browser still displays /blog/nextjs-rewrites.

There’s no redirect, no additional request, and no visible change in the URL.

Why rewrites matter for architecture

A URL is effectively a public contract. Once users, search engines, or external systems depend on it, changing that URL becomes costly.

Rewrites let you maintain that contract while restructuring everything underneath.

This allows you to:

  • Refactor directory structures without breaking links
  • Reorganize routes safely
  • Gradually evolve your application
  • Separate implementation details from public-facing URLs

Using rewrites as an API proxy

A highly practical use case for rewrites is proxying API requests.

module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://external-service.com/:path*',
},
]
},
}

Now, your frontend calls:

/api/users

But the request is internally forwarded to:

https://external-service.com/users

This approach:

  • Prevents CORS complications
  • Keeps API keys on the server
  • Provides a unified API surface for the frontend
  • Allows backend services to change without requiring frontend updates
  • From the client’s perspective, everything lives neatly under /api.

Rewrites vs. redirects (an important distinction)

Redirects

  • Change the URL in the browser
  • Trigger an additional request
  • Are visible to users
  • Influence SEO
Recall (Next.js redirect)
module.exports = {
async redirects() {
return [
{
source: '/shop/:slug',
destination: '/:slug',
permanent: true
},
]
},
}

This creates a redirect rule in Next.js.

It says:

Whenever someone visits /shop/something, redirect them to /something.

What permanent: true means?

This makes it a 308 permanent redirect (similar to a 301 in intent).

Rewrites

  • Preserve the original URL
  • Execute internally
  • Are invisible to users
  • Do not cause navigation
  • If redirects manage navigation, rewrites manage infrastructure.

Where rewrites fit in the request lifecycle

Rewrites are processed before routing occurs.

The flow looks like this:

  1. Next.js checks for rewrites
  2. It determines the final route
  3. It runs the page or API logic

Because rewrites execute early:

  • req.url may not match the actual route being served
  • Middleware must account for rewritten paths
  • Path-based assumptions can fail if rewrites aren’t considered

Their invisibility is what makes them powerful — and occasionally tricky.

Conditional rewrites

Rewrites can also depend on conditions, such as request headers:

{
source: '/dashboard',
has: [
{
type: 'header',
key: 'x-admin',
value: 'true',
},
],
destination: '/admin/dashboard',
}

The same URL can resolve differently based on context.

This makes it possible to support:

  • Role-based routing
  • Multi-tenant systems
  • Feature flagging
  • Environment-specific routing

Why rewrites deserve more attention

Rewrites don’t show up in your UI. They don’t live in components. They don’t advertise their presence.

But they:

  • Separate public URLs from internal structure
  • Make refactoring safer
  • Allow Next.js to function as a lightweight gateway
  • Move routing decisions closer to infrastructure

When you understand rewrites, you stop treating URLs as file paths and start seeing them as stable interfaces.

That perspective changes how you architect applications.

Conclusion

Rewrites aren’t about convenience — they’re about control.

They let your Next.js application grow, migrate, and adapt without disrupting users or external systems. For anything beyond a small project, rewrites aren’t optional. They’re foundational.

Many developers stay at the surface of Next.js.

Rewrites are one of the features that reveal how deep the framework really goes.