Middleware
Middleware ermöglicht es Ihnen, Code auszuführen, bevor eine Anfrage abgeschlossen ist. Basierend auf der eingehenden Anfrage können Sie dann die Antwort durch Umschreiben, Weiterleiten, Modifizieren der Anfrage- oder Antwort-Header oder direktes Antworten anpassen.
Middleware wird ausgeführt, bevor zwischengespeicherte Inhalte und Routen abgeglichen werden. Weitere Details finden Sie unter Pfade abgleichen.
Anwendungsfälle
Einige gängige Szenarien, in denen Middleware effektiv ist, umfassen:
- Schnelle Weiterleitungen nach dem Lesen von Teilen der eingehenden Anfrage
- Umschreiben auf verschiedene Seiten basierend auf A/B-Tests oder Experimenten
- Modifizieren von Headern für alle Seiten oder eine Teilmenge von Seiten
Middleware ist nicht geeignet für:
- Langsame Datenabfragen
- Sitzungsverwaltung
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. Unterteilen Sie Middleware-Funktionalitäten in separate.ts
- oder.js
-Dateien und importieren Sie sie in Ihre Haupt-middleware.ts
-Datei. Dies ermöglicht eine übersichtlichere Verwaltung von routenspezifischer Middleware, die in dermiddleware.ts
für zentrale Kontrolle zusammengefasst wird. Durch die Durchsetzung einer einzigen Middleware-Datei wird die Konfiguration vereinfacht, potenzielle Konflikte vermieden und die Leistung durch die Vermeidung mehrerer Middleware-Schichten optimiert.
Beispiel
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// Diese Funktion kann als `async` markiert werden, wenn `await` im Inneren verwendet wird
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// Siehe "Pfade abgleichen" unten, um mehr zu erfahren
export const config = {
matcher: '/about/:path*',
}
import { NextResponse } from 'next/server'
// Diese Funktion kann als `async` markiert werden, wenn `await` im Inneren verwendet wird
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
// Siehe "Pfade abgleichen" unten, um mehr zu erfahren
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 bestimmte Routen gezielt anzusprechen oder auszuschließen. Die folgende Ausführungsreihenfolge gilt:
headers
ausnext.config.js
redirects
ausnext.config.js
- Middleware (
rewrites
,redirects
, etc.) beforeFiles
(rewrites
) ausnext.config.js
- Dateisystemrouten (
public/
,_next/static/
,pages/
,app/
, etc.) afterFiles
(rewrites
) ausnext.config.js
- Dynamische Routen (
/blog/[slug]
) fallback
(rewrites
) ausnext.config.js
Es gibt zwei Möglichkeiten zu definieren, auf welchen Pfaden Middleware ausgeführt wird:
Matcher
matcher
ermöglicht es Ihnen, Middleware auf bestimmte Pfade zu filtern.
export const config = {
matcher: '/about/:path*',
}
Sie können einen einzelnen Pfad oder mehrere Pfade mit einer Array-Syntax abgleichen:
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
Die matcher
-Konfiguration erlaubt vollständige Regex, sodass Abgleiche wie negative Lookaheads oder Zeichenabgleiche unterstützt werden. Ein Beispiel für einen negativen Lookahead, um alle Pfade außer bestimmten abzugleichen, ist hier zu sehen:
export const config = {
matcher: [
/*
* Alle Anfragepfade abgleichen, außer denen, die mit folgenden beginnen:
* - 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 missing
- oder has
-Arrays oder eine Kombination aus beiden verwenden:
export const config = {
matcher: [
/*
* Alle Anfragepfade abgleichen, außer denen, die mit folgenden beginnen:
* - 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' }],
},
],
}
Gut zu wissen: Die
matcher
-Werte müssen Konstanten sein, damit sie zur Build-Zeit statisch analysiert werden können. Dynamische Werte wie Variablen werden ignoriert.
Konfigurierte Matcher:
- MÜSSEN mit
/
beginnen - Können benannte Parameter enthalten:
/about/:path
passt auf/about/a
und/about/b
, aber nicht auf/about/a/c
- Können Modifikatoren für benannte Parameter haben (beginnend mit
:
):/about/:path*
passt auf/about/a/b/c
, weil*
null oder mehr bedeutet.?
bedeutet null oder eins und+
eins oder mehr - 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.
Gut zu wissen: Aus Gründen der Abwärtskompatibilität betrachtet Next.js
/public
immer als/public/index
. Daher passt ein Matcher von/public/:path
darauf.
Bedingte Anweisungen
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))
}
}
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 auf eine andere URL umzuleiten (
redirect
) - Die Antwort durch Anzeigen einer bestimmten URL umzuschreiben (
rewrite
) - Anfrage-Header für API-Routen,
getServerSideProps
undrewrite
-Ziele zu setzen - Antwort-Cookies zu setzen
- Antwort-Header zu setzen
Um eine Antwort aus der Middleware zu erzeugen, können Sie:
- Auf eine Route (Page oder Edge API Route) umschreiben, die eine Antwort erzeugt
- 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 Möglichkeit, auf diese Cookies zuzugreifen und sie zu manipulieren, über die cookies
-Erweiterung von NextRequest
und NextResponse
.
- Für eingehende Anfragen bietet
cookies
folgende Methoden:get
,getAll
,set
unddelete
Cookies. Sie können das Vorhandensein eines Cookies mithas
überprüfen oder alle Cookies mitclear
entfernen. - Für ausgehende Antworten bietet
cookies
folgende Methoden:get
,getAll
,set
unddelete
.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Angenommen, ein "Cookie:nextjs=fast"-Header ist in der eingehenden Anfrage vorhanden
// Cookies aus der Anfrage mit der `RequestCookies`-API abrufen
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 mit der `ResponseCookies`-API setzen
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 hat einen `Set-Cookie:vercel=fast;path=/`-Header.
return response
}
import { NextResponse } from 'next/server'
export function middleware(request) {
// Angenommen, ein "Cookie:nextjs=fast"-Header ist in der eingehenden Anfrage vorhanden
// Cookies aus der Anfrage mit der `RequestCookies`-API abrufen
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 mit der `ResponseCookies`-API setzen
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 hat einen `Set-Cookie:vercel=fast;path=/test`-Header.
return response
}
Header setzen
Sie können Anfrage- und Antwort-Header mit der NextResponse
-API setzen (das Setzen von Anfrage-Headern ist seit Next.js v13.0.0 verfügbar).
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Die 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
}
import { NextResponse } from 'next/server'
export function middleware(request) {
// Die 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
}
Gut zu wissen: Vermeiden Sie das Setzen großer Header, da dies je nach Konfiguration Ihres Backend-Webservers zu einem 431 Request Header Fields Too Large-Fehler führen kann.
CORS
Sie können CORS-Header in der Middleware setzen, um Cross-Origin-Anfragen zu ermöglichen, einschließlich einfacher und preflight-Anfragen.
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) {
// Die Herkunft aus der Anfrage überprüfen
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Preflight-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*',
}
import { 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) {
// Die Herkunft aus der Anfrage überprüfen
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Preflight-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*',
}
Erzeugen einer Antwort
Sie können direkt aus der Middleware heraus antworten, indem Sie eine Response
- oder NextResponse
-Instanz zurückgeben. (Dies ist seit Next.js v13.1.0 verfügbar)
import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Beschränke die Middleware auf Pfade, die mit `/api/` beginnen
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Rufe unsere Authentifizierungsfunktion auf, um die Anfrage zu prüfen
if (!isAuthenticated(request)) {
// Antworte mit JSON, das eine Fehlermeldung anzeigt
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
import { isAuthenticated } from '@lib/auth'
// Beschränke die Middleware auf Pfade, die mit `/api/` beginnen
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
// Rufe unsere Authentifizierungsfunktion auf, um die Anfrage zu prüfen
if (!isAuthenticated(request)) {
// Antworte mit JSON, 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 erfüllt ist. Dies ist nützlich für Hintergrundarbeiten.
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 v13.1
von Next.js wurden zwei zusätzliche Flags für Middleware eingeführt: skipMiddlewareUrlNormalize
und skipTrailingSlashRedirect
für erweiterte Anwendungsfälle.
skipTrailingSlashRedirect
deaktiviert Next.js-Weiterleitungen für das Hinzufügen oder Entfernen von nachgestellten Schrägstrichen. Dies ermöglicht eine benutzerdefinierte Behandlung innerhalb der Middleware, um den nachgestellten Schrägstrich für einige Pfade beizubehalten, aber nicht für andere, was inkrementelle Migrationen erleichtern kann.
module.exports = {
skipTrailingSlashRedirect: true,
}
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 nachgestellter Schrägstriche anwenden
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 erweiterten Fällen bietet diese Option die vollständige Kontrolle durch Verwendung der ursprünglichen URL.
module.exports = {
skipMiddlewareUrlNormalize: true,
}
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 zu /hello normalisiert werden
}
Unit-Tests (experimentell)
Ab Next.js 15.1 enthält das Paket next/experimental/testing/server
Hilfsmittel zum Unit-Testen von Middleware-Dateien. Unit-Tests für Middleware können sicherstellen, dass sie nur auf gewünschten Pfaden ausgeführt wird und dass benutzerdefinierte Routing-Logik wie beabsichtigt funktioniert, bevor Code in die Produktion gelangt.
Die Funktion unstable_doesMiddlewareMatch
kann verwendet werden, um zu prüfen, ob die Middleware für die bereitgestellte URL, Header und Cookies ausgeführt wird.
import { unstable_doesMiddlewareMatch } from 'next/experimental/testing/server'
expect(
unstable_doesMiddlewareMatch({
config,
nextConfig,
url: '/test',
})
).toEqual(false)
Die gesamte Middleware-Funktion kann ebenfalls getestet werden.
import { isRewrite, getRewrittenUrl } from 'next/experimental/testing/server'
const request = new NextRequest('https://nextjs.org/docs')
const response = await middleware(request)
expect(isRewrite(response)).toEqual(true)
expect(getRewrittenUrl(response)).toEqual('https://other-domain.com/docs')
// getRedirectUrl könnte auch verwendet werden, wenn die Antwort eine Weiterleitung wäre
Laufzeitumgebung
Middleware verwendet standardmäßig die Edge-Laufzeitumgebung. Ab v15.2 (canary) gibt es experimentelle Unterstützung für die Node.js-Laufzeitumgebung. Um dies zu aktivieren, fügen Sie das Flag zu Ihrer next.config
-Datei hinzu:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
nodeMiddleware: true,
},
}
export default nextConfig
const nextConfig = {
experimental: {
nodeMiddleware: true,
},
}
export default nextConfig
Dann setzen Sie in Ihrer Middleware-Datei die Laufzeitumgebung in dem config
-Objekt auf nodejs
:
export const config = {
runtime: 'nodejs',
}
export const config = {
runtime: 'nodejs',
}
Hinweis: Diese Funktion wird für den Produktionseinsatz noch nicht empfohlen. Daher wird Next.js einen Fehler ausgeben, es sei denn, Sie verwenden die next@canary-Version anstelle der stabilen Version.
Plattformunterstützung
Bereitstellungsoption | Unterstützt |
---|---|
Node.js-Server | Ja |
Docker-Container | Ja |
Statischer Export | Nein |
Adapter | Plattformspezifisch |
Erfahren Sie, wie Sie Middleware konfigurieren, wenn Sie Next.js selbst hosten.
Versionsverlauf
Version | Änderungen |
---|---|
v15.2.0 | Middleware kann nun die Node.js-Laufzeitumgebung nutzen (experimentell) |
v13.1.0 | Erweiterte Middleware-Flags hinzugefügt |
v13.0.0 | Middleware kann Anfrage-Header, Antwort-Header modifizieren und Antworten senden |
v12.2.0 | Middleware ist stabil, siehe Upgrade-Guide |
v12.0.9 | Erzwingung absoluter URLs in der Edge-Laufzeitumgebung (PR) |
v12.0.0 | Middleware (Beta) hinzugefügt |