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:
- Next.js checks for rewrites
- It determines the final route
- It runs the page or API logic
Because rewrites execute early:
req.urlmay 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.