Middleware
middleware.ts: Weiterleitungen, Rewrites, Header und Cookies vor jeder Anfrage, mit config.matcher.
Middleware führt Code aus, bevor eine Anfrage abgeschlossen ist. Sie kann weiterleiten, umschreiben, Header und Cookies setzen oder selbst eine Antwort zurückgeben.
Konvention
Erstelle middleware.ts im Projektstammverzeichnis (oder in src/), neben app/. Exportiere eine middleware-Funktion (oder einen Standardexport) und optional eine config.
import { NextResponse, type NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest) { if (!request.cookies.has("session")) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next();}export const config = { matcher: ["/dashboard/:path*", "/api/:path*"],};Middleware läuft vor Route-Handlern, statischen Dateien und Konsolenseiten, unter text dev, text start und jedem serverseitigen Deploy-Adapter. Sie verwendet die Web-APIs Request und Response, sodass dieselbe Datei auf Node, Bun, Vercel Edge und Cloudflare Workers läuft.
Pfade abgleichen
Ohne config.matcher läuft Middleware bei jeder Anfrage. Matcher verwenden dieselbe Syntax wie Next.js:
| Matcher | Treffer |
|---|---|
/about | /about und /about/ |
/blog/:slug | /blog/hello |
/docs/:path* | /docs, /docs/a/b |
/a/:rest+ | /a/b, nicht /a |
/user/:id(\\d+) | /user/42, nicht /user/ada |
/((?!api/).*) | Alles außer /api/* |
Füge mit has und missing Bedingungen hinzu (Header, Cookie, Abfrage oder Host):
export const config = { matcher: [{ source: "/beta/:path*", has: [{ type: "cookie", key: "beta", value: "1" }] }],};Matcher gelten für den Pfadnamen ohne basePath. Unter text dev lösen Vites eigene Modulanfragen (/@vite/*, Quelldateien) niemals Middleware aus.
Jeder Host ermittelt vor der Ausführung der Middleware einen kanonischen Pfadnamen und verwendet ihn für den Abgleich, request.nextUrl und die Suche nach Routen, Handlern und Dateien. Prozentkodierungen werden dekodiert und der Pfad so neu kodiert, wie new URL() ihn schreibt, die Segmente . und .. werden aufgelöst und wiederholte sowie abschließende Schrägstriche entfernt. /ad%6din//users/ erreicht die Middleware als /admin/users, während /café als /caf%C3%A9 erhalten bleibt. Ein Matcher mit abschließendem Schrägstrich, etwa /admin/, trifft auf /admin zu. Anfragen, deren Pfad einen kodierten Schrägstrich oder Rückstrich (%2F, %5C), ein Steuerzeichen oder eine ungültige Prozentkodierung enthält, erhalten 400. Routen unterscheiden Groß- und Kleinschreibung: /Admin passt nicht zu /admin, und statische Dateien werden nur unter ihrem exakten Namen ausgeliefert.
Gleiche die URLs ab, unter denen eine Datei ausgeliefert wird, nicht nur ihren Dateinamen. Cloudflare Pages liefert report.html auch unter /report aus, daher schützt ein Matcher für /report.html nicht /report. Führe beide auf, matcher: ["/report", "/report.html"], oder lege geschützte Dateien in einem Verzeichnis ab und gleiche dieses ab, etwa mit /reports/:path* für Dateien in public/reports/.
NextResponse
| API | Wirkung |
|---|---|
NextResponse.next() | Mit der Route fortfahren |
NextResponse.redirect(url, status?) | Standardmäßig 307; url muss absolut sein |
NextResponse.rewrite(url) | Einen anderen Pfad ausliefern; externe Ursprünge werden über einen Proxy eingebunden |
NextResponse.json(body, init?) | Mit JSON antworten |
new Response(...) | Mit beliebigem Inhalt antworten |
Gib nichts zurück, um unverändert fortzufahren. next() und rewrite() akzeptieren { status }, um den Status der endgültigen Antwort zu ändern. Nur Antworten, die mit NextResponse.next() / NextResponse.rewrite() erstellt wurden, steuern das Routing: Jede andere Response, einschließlich einer von fetch() zurückgegebenen, wird unverändert an den Client gesendet, wobei ihre x-middleware-*-Header entfernt werden.
request.nextUrl schließt basePath aus pathname aus, während href und toString() es enthalten. Um innerhalb der App zu bleiben, klone es: const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url);.
export function middleware(request: NextRequest) { if (request.nextUrl.pathname === "/status") { return NextResponse.rewrite(new URL("/api/health", request.url)); }}Header
Übergib request.headers, um zu ändern, was nachgelagerte Handler empfangen, und headers, um Antwort-Header hinzuzufügen:
export function middleware(request: NextRequest) { const headers = new Headers(request.headers); headers.set("x-user", "ada"); const response = NextResponse.next({ request: { headers } }); response.headers.set("x-frame", "▼"); return response;}Antwort-Header aus der Middleware werden der endgültigen Antwort hinzugefügt, sofern der Route-Handler nicht denselben Header setzt.
Cookies
request.cookies liest den Cookie-Header; response.cookies schreibt Set-Cookie.
export function middleware(request: NextRequest) { const theme = request.cookies.get("theme")?.value ?? "geistDark"; const response = NextResponse.next(); response.cookies.set("theme", theme, { httpOnly: true, sameSite: "lax", maxAge: 60 * 60 * 24 }); return response;}Cookies, die in einer next()- oder rewrite()-Antwort gesetzt werden, sind auch für den Route-Handler innerhalb derselben Anfrage sichtbar.
Hintergrundaufgaben
Das zweite Argument bietet waitUntil(), das serverlose Laufzeitumgebungen nach dem Senden der Antwort aktiv hält:
import type { NextFetchEvent, NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest, event: NextFetchEvent) { event.waitUntil(fetch("https://analytics.example.com", { method: "POST", body: request.url }));}Hinweise
- Clientseitig gerenderte Seiten rendern im Browser, daher ändert eine Umschreibung auf eine andere Seite, welche Datei ausgeliefert wird, nicht welche Route die Konsolen-App rendert. Umschreibungen auf Route-Handler, statische Dateien und externe Ursprünge verhalten sich wie erwartet.
- Middleware kann den Anfrage-Body lesen; Route-Handler erhalten ihn weiterhin. Der Body von Anfragen, die ein Matcher ausschließt, bleibt unangetastet.
request.urlhat immer den Ursprung des Servers: Anfrageziele wie//other.host/xwerden auf einen einzelnen führenden Schrägstrich normalisiert.- Der statische Adapter kann keine Middleware ausführen.