Menu

Middleware

Middleware ermöglicht es Ihnen, Code auszuführen, bevor eine Anfrage abgeschlossen wird. Basierend auf der eingehenden Anfrage können Sie die Antwort ändern, indem Sie Folgendes durchführen: Umschreiben, Umleiten, Ändern der Anfrage- oder Antwortheader oder direkte Antwort.

Middleware wird ausgeführt, bevor zwischengespeicherte Inhalte und Routen abgeglichen werden. Weitere Details finden Sie unter Pfade abgleichen.

Anwendungsfälle

Die Integration von Middleware in Ihre Anwendung kann zu erheblichen Verbesserungen in Bezug auf Leistung, Sicherheit und Benutzererfahrung führen. Einige häufige Szenarien, in denen Middleware besonders effektiv ist, umfassen:

  • Authentifizierung und Autorisierung: Stellen Sie die Benutzeridentität sicher und überprüfen Sie Sitzungs-Cookies, bevor Sie den Zugriff auf bestimmte Seiten oder API-Routen gewähren.
  • Serverseitige Weiterleitungen: Leiten Sie Benutzer auf Serverebene basierend auf bestimmten Bedingungen weiter (z.B. Gebietsschema, Benutzerrolle).
  • Pfad-Umschreibung: Unterstützen Sie A/B-Tests, Feature-Rollouts oder Legacy-Pfade durch dynamisches Umschreiben von Pfaden zu API-Routen oder Seiten basierend auf Anfrageeigenschaften.
  • Bot-Erkennung: Schützen Sie Ihre Ressourcen durch Erkennung und Blockierung von Bot-Verkehr.
  • Logging und Analyse: Erfassen und analysieren Sie Anfragedaten für Erkenntnisse, bevor sie von der Seite oder API verarbeitet werden.
  • Feature-Flagging: Aktivieren oder deaktivieren Sie Features dynamisch für nahtlose Feature-Rollouts oder Tests.

Ebenso wichtig ist es, Situationen zu erkennen, in denen Middleware möglicherweise nicht der optimale Ansatz ist. Hier sind einige Szenarien, bei denen Sie vorsichtig sein sollten:

  • Komplexe Datenabruf und -manipulation: Middleware ist nicht für direktes Datenabrufen oder -manipulieren konzipiert. Dies sollte stattdessen in Route Handlers oder serverseitigen Dienstprogrammen erfolgen.
  • Rechenintensive Aufgaben: Middleware sollte leichtgewichtig sein und schnell reagieren, da es sonst zu Verzögerungen beim Seitenladen kommen kann. Rechenintensive Aufgaben oder Prozesse mit langer Laufzeit sollten in dedizierten Route Handlers durchgeführt werden.
  • Umfangreiches Sitzungsmanagement: Während Middleware grundlegende Sitzungsaufgaben bewältigen kann, sollte ein umfangreiches Sitzungsmanagement von dedizierten Authentifizierungsdiensten oder in Route Handlers durchgeführt werden.
  • Direkte Datenbankoperationen: Direkte Datenbankoperationen in der Middleware werden nicht empfohlen. Datenbankinteraktionen sollten in Route Handlers oder serverseitigen Dienstprogrammen erfolgen.

Konvention

Verwenden Sie die Datei middleware.ts (oder .js) im Stammverzeichnis Ihres Projekts, um Middleware zu definieren. Beispielsweise auf derselben Ebene wie pages oder app oder innerhalb von src, falls zutreffend.

Hinweis: Obwohl nur eine middleware.ts-Datei pro Projekt unterstützt wird, können Sie Ihre Middleware-Logik dennoch modular organisieren. Lagern Sie Middleware-Funktionalitäten in separate .ts- oder .js-Dateien aus und importieren Sie sie in Ihre Haupt-middleware.ts-Datei. Dies ermöglicht eine übersichtlichere Verwaltung von routenspezifischer Middleware, die in der middleware.ts zusammengeführt wird, um eine zentrale Steuerung zu gewährleisten. Durch die Durchsetzung einer einzelnen Middleware-Datei wird die Konfiguration vereinfacht, potenzielle Konflikte werden vermieden und die Leistung wird durch die Vermeidung mehrerer Middleware-Ebenen optimiert.

Beispiel

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
// Diese Funktion kann mit `async` markiert werden, wenn `await` innerhalb verwendet wird
export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url))
}
 
// Weitere Details finden Sie unter "Pfade abgleichen" unten
export const config = {
  matcher: '/about/:path*',
}
middleware.js
import { NextResponse } from 'next/server'
 
// Diese Funktion kann mit `async` markiert werden, wenn `await` innerhalb verwendet wird
export function middleware(request) {
  return NextResponse.redirect(new URL('/home', request.url))
}
 
// Weitere Details finden Sie unter "Pfade abgleichen" unten
export const config = {
  matcher: '/about/:path*',
}

Pfade abgleichen

Middleware wird für jede Route in Ihrem Projekt aufgerufen. Angesichts dessen ist es entscheidend, Matcher zu verwenden, um spezifische Routen präzise auszuwählen oder auszuschließen. Die folgende Reihenfolge wird ausgeführt:

  1. headers aus next.config.js
  2. redirects aus next.config.js
  3. Middleware (rewrites, redirects usw.)
  4. beforeFiles (rewrites) aus next.config.js
  5. Dateisystemrouten (public/, _next/static/, pages/, app/ usw.)
  6. afterFiles (rewrites) aus next.config.js
  7. Dynamische Routen (/blog/[slug])
  8. fallback (rewrites) aus next.config.js

Es gibt zwei Möglichkeiten, zu definieren, auf welchen Pfaden Middleware ausgeführt wird:

  1. Benutzerdefinierte Matcher-Konfiguration
  2. Bedingte Anweisungen

Matcher

matcher ermöglicht es Ihnen, Middleware so zu filtern, dass sie nur auf bestimmten Pfaden ausgeführt wird.

middleware.js
export const config = {
  matcher: '/about/:path*',
}

Sie können einen einzelnen Pfad oder mehrere Pfade mit einer Array-Syntax abgleichen:

middleware.js
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
}

Die matcher-Konfiguration unterstützt vollständige Regex, sodass Matching wie negative Lookaheads oder Zeichenabgleich unterstützt werden. Ein Beispiel für einen negativen Lookahead zum Abgleichen aller Pfade außer bestimmten Pfaden finden Sie hier:

middleware.js
export const config = {
  matcher: [
    /*
     * Gleicht alle Anfragepfade ab, außer denen, die beginnen mit:
     * - api (API-Routen)
     * - _next/static (statische Dateien)
     * - _next/image (Bildoptimierungsdateien)
     * - favicon.ico, sitemap.xml, robots.txt (Metadaten-Dateien)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
  ],
}

Sie können Middleware für bestimmte Anfragen auch umgehen, indem Sie die Arrays missing oder has oder eine Kombination von beiden verwenden:

middleware.js
export const config = {
  matcher: [
    /*
     * Gleicht alle Anfragepfade ab, außer denen, die beginnen mit:
     * - api (API-Routen)
     * - _next/static (statische Dateien)
     * - _next/image (Bildoptimierungsdateien)
     * - favicon.ico, sitemap.xml, robots.txt (Metadaten-Dateien)
     */
    {
      source:
        '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
 
    {
      source:
        '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      has: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
 
    {
      source:
        '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      has: [{ type: 'header', key: 'x-present' }],
      missing: [{ type: 'header', key: 'x-missing', value: 'prefetch' }],
    },
  ],
}

Hinweis: Die matcher-Werte müssen Konstanten sein, damit sie zur Bauzeit statisch analysiert werden können. Dynamische Werte wie Variablen werden ignoriert.

Konfigurierte Matcher:

  1. MÜSSEN mit / beginnen
  2. Können benannte Parameter enthalten: /about/:path passt auf /about/a und /about/b, aber nicht auf /about/a/c
  3. Können Modifikatoren für benannte Parameter haben (beginnend mit :): /about/:path* passt auf /about/a/b/c, da * null oder mehr bedeutet. ? ist null oder eins und + eins oder mehr
  4. Können reguläre Ausdrücke in Klammern verwenden: /about/(.*) ist dasselbe wie /about/:path*

Weitere Details finden Sie in der path-to-regexp Dokumentation.

Hinweis: Aus Gründen der Abwärtskompatibilität betrachtet Next.js /public immer als /public/index. Daher wird ein Matcher von /public/:path übereinstimmen.

Bedingte Anweisungen

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }
 
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}
middleware.js
import { NextResponse } from 'next/server'
 
export function middleware(request) {
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }
 
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}

NextResponse

Die NextResponse-API ermöglicht es Ihnen:

  • Die eingehende Anfrage zu einer anderen URL umzuleiten (redirect)
  • Die Antwort umzuschreiben, indem eine bestimmte URL angezeigt wird (rewrite)
  • Anfrage-Header für API-Routen, getServerSideProps und rewrite-Ziele zu setzen
  • Antwort-Cookies zu setzen
  • Antwort-Header zu setzen

Um eine Antwort aus Middleware zu erzeugen, können Sie:

  1. Zu einer Route (rewrite) (Seite oder Routenhandler) wechseln, die eine Antwort erzeugt
  2. Direkt eine NextResponse zurückgeben. Siehe Antwort erzeugen

Cookies verwenden

Cookies sind reguläre Header. Bei einer Request werden sie im Cookie-Header gespeichert. Bei einer Response befinden sie sich im Set-Cookie-Header. Next.js bietet eine bequeme Methode, diese Cookies über die cookies-Erweiterung von NextRequest und NextResponse zu verwenden.

  1. Für eingehende Anfragen verfügt cookies über folgende Methoden: get, getAll, set und delete von Cookies. Sie können das Vorhandensein eines Cookies mit has überprüfen oder alle Cookies mit clear entfernen.
  2. Für ausgehende Antworten haben cookies folgende Methoden: get, getAll, set und delete.
middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  // Es wird angenommen, dass ein Header "Cookie:nextjs=fast" in der eingehenden Anfrage vorhanden ist
  // Cookies von der Anfrage abrufen mithilfe der `RequestCookies`-API
  let cookie = request.cookies.get('nextjs')
  console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
  const allCookies = request.cookies.getAll()
  console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
 
  request.cookies.has('nextjs') // => true
  request.cookies.delete('nextjs')
  request.cookies.has('nextjs') // => false
 
  // Cookies in der Antwort setzen mithilfe der `ResponseCookies`-API
  const response = NextResponse.next()
  response.cookies.set('vercel', 'fast')
  response.cookies.set({
    name: 'vercel',
    value: 'fast',
    path: '/',
  })
  cookie = response.cookies.get('vercel')
  console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
  // Die ausgehende Antwort wird einen Header `Set-Cookie:vercel=fast;path=/` haben.
 
  return response
}
middleware.js
import { NextResponse } from 'next/server'
 
export function middleware(request) {
  // Es wird angenommen, dass ein Header "Cookie:nextjs=fast" in der eingehenden Anfrage vorhanden ist
  // Cookies von der Anfrage abrufen mithilfe der `RequestCookies`-API
  let cookie = request.cookies.get('nextjs')
  console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
  const allCookies = request.cookies.getAll()
  console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
 
  request.cookies.has('nextjs') // => true
  request.cookies.delete('nextjs')
  request.cookies.has('nextjs') // => false
 
  // Cookies in der Antwort setzen mithilfe der `ResponseCookies`-API
  const response = NextResponse.next()
  response.cookies.set('vercel', 'fast')
  response.cookies.set({
    name: 'vercel',
    value: 'fast',
    path: '/',
  })
  cookie = response.cookies.get('vercel')
  console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
  // Die ausgehende Antwort wird einen Header `Set-Cookie:vercel=fast;path=/test` haben.
 
  return response
}

Header setzen

Sie können Anfrage- und Antwort-Header mithilfe der NextResponse-API setzen (das Setzen von Anfrage-Headern ist seit Next.js v13.0.0 verfügbar).

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  // Anfrage-Header klonen und einen neuen Header `x-hello-from-middleware1` setzen
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-middleware1', 'hello')
 
  // Sie können Anfrage-Header auch in NextResponse.next setzen
  const response = NextResponse.next({
    request: {
      // Neue Anfrage-Header
      headers: requestHeaders,
    },
  })
 
  // Einen neuen Antwort-Header `x-hello-from-middleware2` setzen
  response.headers.set('x-hello-from-middleware2', 'hello')
  return response
}
middleware.js
import { NextResponse } from 'next/server'
 
export function middleware(request) {
  // Anfrage-Header klonen und einen neuen Header `x-hello-from-middleware1` setzen
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-middleware1', 'hello')
 
  // Sie können Anfrage-Header auch in NextResponse.next setzen
  const response = NextResponse.next({
    request: {
      // Neue Anfrage-Header
      headers: requestHeaders,
    },
  })
 
  // Einen neuen Antwort-Header `x-hello-from-middleware2` setzen
  response.headers.set('x-hello-from-middleware2', 'hello')
  return response
}

Hinweis: Vermeiden Sie das Setzen großer Header, da dies je nach Konfiguration Ihres Backend-Webservers zu einem Fehler 431 Request Header Fields Too Large führen kann.

CORS

Sie können CORS-Header in Middleware setzen, um eingehende Anfragen von anderen Ursprüngen zu ermöglichen, einschließlich einfacher und vorausgeprüfter Anfragen.

middleware.ts
TypeScript
import { NextRequest, NextResponse } from 'next/server'
 
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
 
const corsOptions = {
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
 
export function middleware(request: NextRequest) {
  // Ursprung aus der Anfrage überprüfen
  const origin = request.headers.get('origin') ?? ''
  const isAllowedOrigin = allowedOrigins.includes(origin)
 
  // Vorausgeprüfte Anfragen behandeln
  const isPreflight = request.method === 'OPTIONS'
 
  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
      ...corsOptions,
    }
    return NextResponse.json({}, { headers: preflightHeaders })
  }
 
  // Einfache Anfragen behandeln
  const response = NextResponse.next()
 
  if (isAllowedOrigin) {
    response.headers.set('Access-Control-Allow-Origin', origin)
  }
 
  Object.entries(corsOptions).forEach(([key, value]) => {
    response.headers.set(key, value)
  })
 
  return response
}
 
export const config = {
  matcher: '/api/:path*',
}

Hinweis: Sie können CORS-Header für einzelne Routen in Route Handlers konfigurieren.

Eine Antwort erzeugen

Sie können direkt aus der Middleware eine Antwort zurückgeben, indem Sie eine Response- oder NextResponse-Instanz zurückgeben. (Dies ist seit Next.js v13.1.0 verfügbar)

middleware.ts
import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
 
// Die Middleware auf Pfade beschränken, die mit `/api/` beginnen
export const config = {
  matcher: '/api/:function*',
}
 
export function middleware(request: NextRequest) {
  // Unsere Authentifizierungsfunktion aufrufen, um die Anfrage zu überprüfen
  if (!isAuthenticated(request)) {
    // Mit JSON antworten, das eine Fehlermeldung anzeigt
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}
middleware.js
import { isAuthenticated } from '@lib/auth'
 
// Die Middleware auf Pfade beschränken, die mit `/api/` beginnen
export const config = {
  matcher: '/api/:function*',
}
 
export function middleware(request) {
  // Unsere Authentifizierungsfunktion aufrufen, um die Anfrage zu überprüfen
  if (!isAuthenticated(request)) {
    // Mit JSON antworten, das eine Fehlermeldung anzeigt
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}

waitUntil und NextFetchEvent

Das NextFetchEvent-Objekt erweitert das native FetchEvent-Objekt und enthält die waitUntil()-Methode.

Die waitUntil()-Methode nimmt ein Promise als Argument und verlängert die Lebensdauer der Middleware, bis das Promise abgeschlossen ist. Dies ist nützlich, um Arbeiten im Hintergrund auszuführen.

middleware.ts
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
 
export function middleware(req: NextRequest, event: NextFetchEvent) {
  event.waitUntil(
    fetch('https://my-analytics-platform.com', {
      method: 'POST',
      body: JSON.stringify({ pathname: req.nextUrl.pathname }),
    })
  )
 
  return NextResponse.next()
}

Erweiterte Middleware-Flags

In Next.js v13.1 wurden zwei zusätzliche Flags für Middleware eingeführt: skipMiddlewareUrlNormalize und skipTrailingSlashRedirect, um fortgeschrittene Anwendungsfälle zu behandeln.

skipTrailingSlashRedirect deaktiviert Next.js-Weiterleitungen zum Hinzufügen oder Entfernen von abschließenden Schrägstrichen. Dies ermöglicht eine benutzerdefinierte Behandlung in der Middleware, um abschließende Schrägstriche für einige Pfade, aber nicht für andere beizubehalten, was schrittweise Migrationen erleichtern kann.

next.config.js
module.exports = {
  skipTrailingSlashRedirect: true,
}
middleware.js
const legacyPrefixes = ['/docs', '/blog']
 
export default async function middleware(req) {
  const { pathname } = req.nextUrl
 
  if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
    return NextResponse.next()
  }
 
  // Behandlung abschließender Schrägstriche
  if (
    !pathname.endsWith('/') &&
    !pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
  ) {
    return NextResponse.redirect(
      new URL(`${req.nextUrl.pathname}/`, req.nextUrl)
    )
  }
}

skipMiddlewareUrlNormalize ermöglicht das Deaktivieren der URL-Normalisierung in Next.js, um direkte Besuche und Client-Übergänge gleich zu behandeln. In einigen fortgeschrittenen Fällen bietet diese Option eine vollständige Kontrolle durch Verwendung der ursprünglichen URL.

next.config.js
module.exports = {
  skipMiddlewareUrlNormalize: true,
}
middleware.js
export default async function middleware(req) {
  const { pathname } = req.nextUrl
 
  // GET /_next/data/build-id/hello.json
 
  console.log(pathname)
  // Mit dem Flag ist dies nun /_next/data/build-id/hello.json
  // Ohne das Flag würde dies auf /hello normalisiert werden
}

Laufzeit

Middleware unterstützt derzeit nur mit der Edge-Laufzeit kompatible APIs. APIs, die ausschließlich zu Node.js gehören, werden nicht unterstützt.

Versionshistorie

VersionÄnderungen
v13.1.0Erweiterte Middleware-Flags hinzugefügt
v13.0.0Middleware kann Anfrage-Header, Antwort-Header ändern und Antworten senden
v12.2.0Middleware ist stabil, bitte lesen Sie den Upgrade-Leitfaden
v12.0.9Absolute URLs in Edge-Laufzeit erzwingen (PR)
v12.0.0Middleware (Beta) hinzugefügt