Server Side Next.js Redirect: The Complete SEO Implementation Guide

Use permanentRedirect() for 308 permanent redirects or redirect() for 307 temporary redirects from next/navigation in App Router Server Components and Route Handlers, or NextResponse.redirect() with explicit status codes in Middleware, to ensure search engines receive proper HTTP status headers without executing client-side JavaScript.

Implementing a server side Next.js redirect is critical for preserving search rankings when migrating URLs or restructuring content. According to the vercel/next.js source code, the framework provides optimized helpers that issue HTTP-level redirects with correct status codes, eliminating client-side round trips that can confuse search engine crawlers.

App Router: Server Components and Route Handlers

The App Router provides dedicated navigation utilities in packages/next/src/client/components/redirect.ts that throw RedirectError instances caught by the runtime to generate HTTP responses.

Permanent Redirects with permanentRedirect()

For SEO-friendly permanent moves, use permanentRedirect() which returns a 308 Permanent Redirect status. In packages/next/src/client/components/redirect-status-code.ts, this maps to RedirectStatusCode.PermanentRedirect.

// app/old-page/page.tsx
import { permanentRedirect } from 'next/navigation'

export default function OldPage() {
  // Throws RedirectError, caught by runtime, returns 308 with Location header
  permanentRedirect('/new-page')
}

Temporary Redirects with redirect()

For temporary moves, redirect() issues a 307 Temporary Redirect by default, preserving the HTTP method used in the original request.

// app/api/redirect/route.ts
import { redirect } from 'next/navigation'

export async function GET() {
  // 307 temporary redirect
  redirect('/temporary-destination')
}

Advanced: Using getRedirectError Directly

For custom logic requiring manual error throwing, use the internal getRedirectError helper. This achieves the same result as redirect() but allows dynamic status code configuration.

// lib/redirect-utils.ts
import { getRedirectError } from 'next/navigation'

export function redirectTo(url: string) {
  // Manually throw the internal error – same effect as `redirect()`
  throw getRedirectError(url, 'replace', 307)
}

Middleware and Edge Runtime

For redirects that must execute before hitting application logic, Next.js Middleware running in the Edge Runtime uses NextResponse.redirect() as shown in packages/next/src/server/web/error.ts.

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

export function middleware(request) {
  const url = new URL(request.url)
  if (url.pathname === '/old-blog') {
    // Explicit 301 permanent redirect
    return NextResponse.redirect(new URL('/new-blog', request.url), { status: 301 })
  }
  return NextResponse.next()
}

This approach allows precise control over status codes (301 or 302) while executing at the edge, enabling instant response caching without invoking Node.js server processes.

Pages Router API Routes

For the legacy Pages Router, implement redirects using the Node.js response object directly when running outside the Edge Runtime.

// pages/api/old.ts
export default function handler(req, res) {
  res.writeHead(301, { Location: '/new' })
  res.end()
}

Why Server-Side Redirects Matter for SEO

Using the built-in server side Next.js redirect APIs provides specific search engine optimization advantages over client-side navigation:

  • No Client-Side Execution: Crawlers receive the Location header immediately without executing JavaScript, ensuring the redirect is followed correctly and indexation targets the destination URL.
  • Correct Status Codes: The 308 permanent redirect (used by permanentRedirect()) preserves POST request methods during the redirect, while 301 remains available in Middleware for classic permanent redirects that method-shift to GET.
  • Edge Caching: Next.js caches redirect responses in the Edge Runtime, reducing latency for subsequent crawler requests and minimizing server load.

Summary

  • Use permanentRedirect() from next/navigation for 308 permanent redirects in App Router Server Components, as implemented in packages/next/src/client/components/redirect.ts.
  • Use redirect() from next/navigation for 307 temporary redirects in Route Handlers.
  • Use NextResponse.redirect() in Middleware for edge-level redirects with customizable 301/302 status codes.
  • Reference packages/next/src/client/components/redirect-status-code.ts for the enum definitions of supported status codes.
  • Always prefer server-side redirects over useRouter() client-side navigation for SEO-critical URL changes to ensure search engines properly transfer ranking signals.

Frequently Asked Questions

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

Next.js supports both status codes: 301 (classic permanent redirect) available in Middleware via NextResponse.redirect(), and 308 (permanent redirect) used by permanentRedirect() in the App Router. While both indicate permanent moves, 308 preserves the original HTTP method (POST stays POST), whereas 301 historically causes clients to switch POST requests to GET.

Can I use redirect() in a Client Component?

No. The redirect() and permanentRedirect() functions from next/navigation are designed for Server Components and Route Handlers only. They function by throwing a RedirectError that the server runtime catches and converts into an HTTP response. For client-side navigation, use useRouter(), though this lacks the SEO benefits of server-side redirects.

How do I handle dynamic URL redirects in Next.js Middleware?

Inspect the request.nextUrl pathname in your middleware.ts file, then construct the target URL dynamically before calling NextResponse.redirect(). This executes at the edge before the request reaches your application code, making it efficient for high-volume redirect rules and A/B testing scenarios.

Do server-side redirects in Next.js affect page performance?

Server-side redirects improve perceived performance by eliminating the client-side JavaScript execution required for client-side navigation. When implemented in Middleware, the Edge Runtime caches redirect responses, meaning subsequent requests receive the Location header instantly without invoking Node.js server processes or database queries.

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 →