Weiterleitungen

Es gibt mehrere Möglichkeiten, Weiterleitungen in Next.js zu handhaben. Diese Seite stellt jede verfügbare Option vor, erläutert Anwendungsfälle und zeigt, wie Sie eine große Anzahl von Weiterleitungen verwalten können.

APIZweckVerwendungsortStatuscode
useRouterClient-seitige Navigation durchführenComponentsN/A
redirects in next.config.jsAnfrage basierend auf Pfad umleitennext.config.js Datei307 (Temporär) oder 308 (Permanent)
NextResponse.redirectAnfrage basierend auf Bedingung umleitenMiddlewareBeliebig

useRouter()-Hook

Für Weiterleitungen in Components können Sie die push-Methode des useRouter-Hooks verwenden. Beispiel:

import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}
import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

Gut zu wissen:

  • Für nicht-programmatische Navigation sollten Sie eine <Link>-Komponente verwenden.

Weitere Informationen finden Sie in der useRouter-API-Referenz.

redirects in next.config.js

Die redirects-Option in der next.config.js-Datei ermöglicht es, eingehende Anfragen basierend auf dem Pfad umzuleiten. Dies ist nützlich bei Änderungen der URL-Struktur oder bei bekannten Weiterleitungen.

redirects unterstützt Pfad, Header-, Cookie- und Query-Matching für flexible Weiterleitungsregeln.

So verwenden Sie redirects:

next.config.js
module.exports = {
  async redirects() {
    return [
      // Einfache Weiterleitung
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
      // Wildcard-Pfad-Matching
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
    ]
  },
}

Weitere Informationen finden Sie in der redirects-API-Referenz.

Gut zu wissen:

  • redirects kann 307 (Temporary Redirect) oder 308 (Permanent Redirect) Statuscodes zurückgeben.
  • Auf einigen Plattformen gibt es Limits (z.B. 1.024 Weiterleitungen auf Vercel). Für große Mengen (1000+) sollten Sie Middleware verwenden. Siehe Weiterleitungen im großen Stil.
  • redirects wird vor Middleware ausgeführt.

NextResponse.redirect in Middleware

Middleware ermöglicht Code-Ausführung vor Abschluss einer Anfrage. Mit NextResponse.redirect können Sie basierend auf der Anfrage umleiten, z.B. für Authentifizierung oder Session-Management.

Beispiel für Authentifizierungs-Weiterleitung:

import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'

export function middleware(request: NextRequest) {
  const isAuthenticated = authenticate(request)

  if (isAuthenticated) {
    return NextResponse.next()
  }

  return NextResponse.redirect(new URL('/login', request.url))
}

export const config = {
  matcher: '/dashboard/:path*',
}
import { NextResponse } from 'next/server'
import { authenticate } from 'auth-provider'

export function middleware(request) {
  const isAuthenticated = authenticate(request)

  if (isAuthenticated) {
    return NextResponse.next()
  }

  return NextResponse.redirect(new URL('/login', request.url))
}

export const config = {
  matcher: '/dashboard/:path*',
}

Gut zu wissen:

  • Middleware wird nach redirects in next.config.js und vor dem Rendering ausgeführt.

Weitere Informationen finden Sie in der Middleware-Dokumentation.

Weiterleitungen im großen Stil (Fortgeschritten)

Für große Mengen an Weiterleitungen (1000+) können Sie eine benutzerdefinierte Middleware-Lösung erstellen, ohne die Anwendung neu zu deployen.

Dafür benötigen Sie:

  1. Eine Weiterleitungs-Map
  2. Optimierte Datenabfrage

Next.js-Beispiel: Siehe unser Middleware mit Bloom-Filter-Beispiel für eine Implementierung.

1. Erstellen und Speichern einer Weiterleitungs-Map

Eine Weiterleitungs-Map ist eine Liste von Weiterleitungen, die in einer Datenbank oder JSON-Datei gespeichert werden kann.

Beispiel-Datenstruktur:

{
  "/old": {
    "destination": "/new",
    "permanent": true
  },
  "/blog/post-old": {
    "destination": "/blog/post-new",
    "permanent": true
  }
}

In Middleware können Sie aus Datenbanken wie Vercels Edge Config oder Redis lesen:

import { NextResponse, NextRequest } from 'next/server'
import { get } from '@vercel/edge-config'

type RedirectEntry = {
  destination: string
  permanent: boolean
}

export async function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname
  const redirectData = await get(pathname)

  if (redirectData && typeof redirectData === 'string') {
    const redirectEntry: RedirectEntry = JSON.parse(redirectData)
    const statusCode = redirectEntry.permanent ? 308 : 307
    return NextResponse.redirect(redirectEntry.destination, statusCode)
  }

  return NextResponse.next()
}
import { NextResponse } from 'next/server'
import { get } from '@vercel/edge-config'

export async function middleware(request) {
  const pathname = request.nextUrl.pathname
  const redirectData = await get(pathname)

  if (redirectData) {
    const redirectEntry = JSON.parse(redirectData)
    const statusCode = redirectEntry.permanent ? 308 : 307
    return NextResponse.redirect(redirectEntry.destination, statusCode)
  }

  return NextResponse.next()
}

2. Optimierung der Datenabfrageleistung

Das Lesen eines großen Datensatzes für jede eingehende Anfrage kann langsam und ressourcenintensiv sein. Es gibt zwei Möglichkeiten, die Leistung bei der Datenabfrage zu optimieren:

  • Verwenden Sie eine Datenbank, die für schnelle Lesevorgänge optimiert ist, wie z.B. Vercel Edge Config oder Redis.
  • Verwenden Sie eine Datenabfragestrategie wie einen Bloom-Filter, um effizient zu prüfen, ob eine Weiterleitung existiert, bevor die größere Weiterleitungsdatei oder Datenbank gelesen wird.

Bezogen auf das vorherige Beispiel können Sie eine generierte Bloom-Filter-Datei in die Middleware importieren und dann prüfen, ob der Pfad der eingehenden Anfrage im Bloom-Filter enthalten ist.

Falls ja, leiten Sie die Anfrage an einen API-Route weiter, der die eigentliche Datei überprüft und den Benutzer zur entsprechenden URL weiterleitet. Dadurch wird vermieden, eine große Weiterleitungsdatei in die Middleware zu importieren, was jede eingehende Anfrage verlangsamen könnte.

import { NextResponse, NextRequest } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'

type RedirectEntry = {
  destination: string
  permanent: boolean
}

// Initialisiere Bloom-Filter aus einer generierten JSON-Datei
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)

export async function middleware(request: NextRequest) {
  // Hole den Pfad der eingehenden Anfrage
  const pathname = request.nextUrl.pathname

  // Prüfe, ob der Pfad im Bloom-Filter enthalten ist
  if (bloomFilter.has(pathname)) {
    // Leite den Pfad an den Route Handler weiter
    const api = new URL(
      `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
      request.nextUrl.origin
    )

    try {
      // Hole Weiterleitungsdaten vom Route Handler
      const redirectData = await fetch(api)

      if (redirectData.ok) {
        const redirectEntry: RedirectEntry | undefined =
          await redirectData.json()

        if (redirectEntry) {
          // Bestimme den Statuscode
          const statusCode = redirectEntry.permanent ? 308 : 307

          // Leite zur Ziel-URL weiter
          return NextResponse.redirect(redirectEntry.destination, statusCode)
        }
      }
    } catch (error) {
      console.error(error)
    }
  }

  // Keine Weiterleitung gefunden, Anfrage ohne Weiterleitung fortsetzen
  return NextResponse.next()
}
import { NextResponse } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'

// Initialisiere Bloom-Filter aus einer generierten JSON-Datei
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter)

export async function middleware(request) {
  // Hole den Pfad der eingehenden Anfrage
  const pathname = request.nextUrl.pathname

  // Prüfe, ob der Pfad im Bloom-Filter enthalten ist
  if (bloomFilter.has(pathname)) {
    // Leite den Pfad an den Route Handler weiter
    const api = new URL(
      `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
      request.nextUrl.origin
    )

    try {
      // Hole Weiterleitungsdaten vom Route Handler
      const redirectData = await fetch(api)

      if (redirectData.ok) {
        const redirectEntry = await redirectData.json()

        if (redirectEntry) {
          // Bestimme den Statuscode
          const statusCode = redirectEntry.permanent ? 308 : 307

          // Leite zur Ziel-URL weiter
          return NextResponse.redirect(redirectEntry.destination, statusCode)
        }
      }
    } catch (error) {
      console.error(error)
    }
  }

  // Keine Weiterleitung gefunden, Anfrage ohne Weiterleitung fortsetzen
  return NextResponse.next()
}

Dann in der API-Route:

import { NextApiRequest, NextApiResponse } from 'next'
import redirects from '@/app/redirects/redirects.json'

type RedirectEntry = {
  destination: string
  permanent: boolean
}

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const pathname = req.query.pathname
  if (!pathname) {
    return res.status(400).json({ message: 'Bad Request' })
  }

  // Hole den Weiterleitungseintrag aus der redirects.json-Datei
  const redirect = (redirects as Record<string, RedirectEntry>)[pathname]

  // Berücksichtige falsch positive Ergebnisse des Bloom-Filters
  if (!redirect) {
    return res.status(400).json({ message: 'No redirect' })
  }

  // Gib den Weiterleitungseintrag zurück
  return res.json(redirect)
}
import redirects from '@/app/redirects/redirects.json'

export default function handler(req, res) {
  const pathname = req.query.pathname
  if (!pathname) {
    return res.status(400).json({ message: 'Bad Request' })
  }

  // Hole den Weiterleitungseintrag aus der redirects.json-Datei
  const redirect = redirects[pathname]

  // Berücksichtige falsch positive Ergebnisse des Bloom-Filters
  if (!redirect) {
    return res.status(400).json({ message: 'No redirect' })
  }

  // Gib den Weiterleitungseintrag zurück
  return res.json(redirect)
}

Gut zu wissen:

  • Um einen Bloom-Filter zu generieren, können Sie eine Bibliothek wie bloom-filters verwenden.
  • Sie sollten Anfragen an Ihren Route Handler validieren, um bösartige Anfragen zu verhindern.