How to Dynamically Change the Browser URL When Using Next.js Rewrites

Next.js rewrites are server-side mappings that intentionally keep the original URL in the browser address bar, so you must use redirects, client-side router methods, or middleware to display the rewritten path.

Next.js rewrites allow you to map incoming request URLs to different internal destinations without exposing the underlying path structure. However, when implementing nextjs rewrites, developers often need the browser to display the destination URL rather than the original request path. Understanding how the framework processes these mappings in packages/next/src/shared/lib/router/utils/resolve-rewrites.ts is essential for choosing the right approach.

Why Next.js Rewrites Preserve the Original URL

Rewrites in Next.js operate entirely on the server side before the request reaches the page rendering logic. When a request matches a rewrite rule defined in next.config.js, the framework internally resolves the destination using the logic found in packages/next/src/shared/lib/router/utils/resolve-rewrites.ts. Because the client receives the response without an HTTP redirect status code, the browser address bar remains on the original request URL.

This design makes rewrites ideal for proxying API routes, serving content from different folders, or masking external services while maintaining a clean URL structure. However, the transparency of rewrites means they cannot alone update the browser's displayed URL.

Three Methods to Display the Rewritten Path in the Address Bar

To surface the rewritten path in the browser address bar, you must move beyond basic rewrites and employ one of three strategies: permanent redirects, client-side navigation, or middleware-based conditional logic.

Use Redirects for Permanent URL Changes

When you want the browser to permanently display the new URL and maintain SEO value, use the redirects configuration in next.config.js. Unlike rewrites, redirects return HTTP 301 or 302 status codes that instruct the browser to navigate to the target URL.

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/old-blog/:slug',
        destination: '/blog/:slug',
        permanent: true, // 308 permanent redirect → address bar shows "/blog/:slug"
      },
    ]
  },
}

Use Client-Side Navigation After Server Rewrites

For scenarios where you want to preserve the rewrite for initial server-side rendering but update the URL once the page loads, combine a rewrite with router.replace or router.push. This approach is useful when the rewritten path represents the canonical URL that should appear in the address bar after hydration.

// pages/blog/[slug].js
import { useRouter } from 'next/router'
import { useEffect } from 'react'

export default function BlogPost({ data }) {
  const router = useRouter()

  useEffect(() => {
    // Replace the URL after the page has been rendered via a rewrite
    router.replace(`/blog/${data.slug}`, undefined, { shallow: true })
  }, [data.slug])

  return <article>{/* render post */}</article>
}
// next.config.js – keep the rewrite for internal handling
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog/:slug',
        destination: '/posts/:slug', // internal data fetch
      },
    ]
  },
}

Use Middleware for Conditional Rewrite and Redirect Logic

For complex routing scenarios where some requests should remain internal while others require URL updates, implement conditional logic in Next.js Middleware. Middleware can examine the request, perform a rewrite for internal handling, and then issue a redirect if the URL should be reflected in the browser.

// middleware.ts
import { NextResponse } from 'next/server'

export function middleware(request) {
  const { pathname } = request.nextUrl

  // Example: rewrite /preview/* to /api/preview/*, then show the preview path
  if (pathname.startsWith('/preview/')) {
    const url = request.nextUrl.clone()
    url.pathname = `/api${pathname}`
    const response = NextResponse.rewrite(url)

    // After rewiring, issue a redirect so the browser shows the preview URL
    const redirect = NextResponse.redirect(request.nextUrl)
    redirect.headers.set('Location', pathname) // keep the original preview path
    return redirect
  }

  return NextResponse.next()
}

How Next.js Processes Rewrites Internally

Understanding the internal implementation helps clarify why rewrites cannot alone update the browser URL. The core logic resides in packages/next/src/shared/lib/router/utils/resolve-rewrites.ts, which parses the rewrite configuration and matches incoming requests against defined patterns.

For the App Router, additional logic in packages/next/src/lib/generate-interception-routes-rewrites.ts generates internal rewrite rules for route interception scenarios. The test suite in packages/next-routing/src/__tests__/rewrites.test.ts validates these behaviors, confirming that rewrites operate as transparent server-side mappings without client-side visibility.

Because the rewrite resolution occurs before the Next.js router initializes on the client, the browser has no indication that the content originated from a different path, keeping the original URL intact in the address bar.

Summary

  • Next.js rewrites are server-side only and intentionally preserve the original URL in the browser address bar to enable transparent path mapping.
  • To display the rewritten path, use redirects in next.config.js for permanent URL changes that update the address bar via HTTP status codes.
  • For dynamic updates after page load, combine rewrites with client-side navigation using router.replace() or router.push() with shallow routing.
  • For complex conditional logic, implement Next.js Middleware to selectively rewrite internal requests while issuing redirects to update the browser URL when necessary.
  • The internal rewrite logic is handled in packages/next/src/shared/lib/router/utils/resolve-rewrites.ts, confirming that rewrites operate before client-side hydration occurs.

Frequently Asked Questions

Do Next.js rewrites change the URL in the browser?

No, Next.js rewrites do not change the URL displayed in the browser address bar. They are designed as transparent server-side mappings that resolve internally in packages/next/src/shared/lib/router/utils/resolve-rewrites.ts before the page renders, keeping the original request URL visible to users.

What is the difference between rewrites and redirects in Next.js?

Rewrites map an incoming URL to a different internal destination without notifying the browser, while redirects return HTTP 301 or 302 status codes that instruct the browser to navigate to a new URL, thereby updating the address bar. Use rewrites for internal path aliasing and redirects when you want users and search engines to recognize the new canonical URL.

Can I use middleware to rewrite a URL and then change the browser address bar?

Yes, Next.js Middleware can combine both operations by first calling NextResponse.rewrite() to handle the request internally, then conditionally returning NextResponse.redirect() to update the browser URL. This approach is useful when you need to rewrite certain requests transparently while exposing the rewritten path for specific routes or user segments.

How do I dynamically rewrite URLs based on request parameters?

You can define dynamic rewrites in next.config.js by using path segments like :slug or :path* in the source pattern, or by providing a function to the destination property that receives the matched parameters. For more complex logic based on headers, cookies, or query strings, implement custom rewrite logic in Next.js Middleware where you have full access to the request object.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →