proxy.ts (Next.js Middleware-Nachfolger)
Kurz: In Next.js 16 wurde middleware.ts in proxy.ts umbenannt — nur eine der beiden Dateien darf im Projekt existieren, sonst bricht der Build.
Genauer: Läuft wie die frühere Middleware vor dem eigentlichen Rendern/Routing und kann Requests umschreiben, umleiten oder früh mit einer eigenen Antwort abbrechen (z. B. 429 bei Rate-Limit-Überschreitung). Der Matcher (config.matcher) legt fest, für welche Pfade die Funktion überhaupt aufgerufen wird.
Kontext bei uns: proxy.ts bei Emzett übernimmt bewusst ZWEI Aufgaben in einer Datei: globales Rate-Limiting (breiter Matcher, fast jeder Request, mit Ausnahme reiner Lese-Seiten ohne Formulare) und einen leichten Auth-Redirect für geschützte Bereiche (nur Cookie-Vorhanden-Check — die volle Session-Verifikation gegen die DB passiert weiterhin serverseitig in getCurrentUser()). Laut AGENTS.md im Projekt-Root ein bewusst dokumentierter Next.js-16-Fallstrick, weil Trainings-Wissen hier noch von middleware.ts ausgeht.
Im Detail
Der Matcher als Performance-Hebel
Der matcher in der exportierten config ist entscheidend für Performance: Läuft die Funktion auf JEDEM Request (inklusive aller statischen Assets), kostet das unnötig Edge-Function-Aufrufe — jeder Aufruf bedeutet zusätzliche Latenz und bei den meisten Hosting-Plattformen auch zusätzliche Kosten. Ein typisches Muster grenzt gezielt aus, was garantiert nie durch die Middleware muss:
export const config = {
matcher: [
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)",
],
};Der Matcher wird intern zu einem regulären Ausdruck kompiliert und VOR jeder Anfrage geprüft — er läuft also selbst dann, wenn die eigentliche Middleware-Funktion aufgrund des Ergebnisses gar nicht ausgeführt wird. Statische Matcher-Strings (wie oben) sind performanter als dynamisch mit Array-Methoden zusammengesetzte, da Next.js sie zur Build-Zeit vollständig analysieren kann.
Umschreiben, Umleiten oder Abbrechen
Innerhalb der Middleware-Funktion stehen im Wesentlichen drei Reaktionsmöglichkeiten zur Verfügung: NextResponse.next() lässt den Request unverändert weiterlaufen, NextResponse.redirect(url) schickt eine echte HTTP-Weiterleitung an den Browser (die URL in der Adressleiste ändert sich sichtbar), und NextResponse.rewrite(url) liefert Inhalt von einer anderen internen URL aus, OHNE dass der Browser davon etwas mitbekommt (die Adressleiste bleibt unverändert). Für Auth-Redirects ist meist redirect() richtig (der Nutzer soll sehen, dass er z. B. auf /login landet), für internes Umschreiben auf statische Exporte oder A/B-Testing-Varianten eher rewrite().
Interaktion mit next.config.ts’s rewrites()
Ein wichtiger, leicht übersehener Punkt: next.config.ts’s rewrites() und die Middleware laufen in unterschiedlichen Phasen der Request-Verarbeitung — wenn ein Pfad über einen Rewrite auf komplett andere, eigenständige statische Inhalte zeigt (z. B. einen fertig gebauten Static-Site-Export), ihn aber der Middleware-Matcher trotzdem erfasst, laufen ALLE nachgeladenen Assets dieser Seite (CSS, JS, JSON) einzeln durch dieselbe Middleware-Logik mit — bei global angewendetem Rate-Limiting kann das schnell das Limit einer einzigen “Seitenansicht” sprengen, weil dutzende Einzel-Requests statt einem gezählt werden. Eine explizite Ausnahme im Matcher oder in der eigenen Middleware-Logik für solche Pfade ist dann nötig.
Grenzen der Edge-Runtime
Middleware/proxy.ts läuft standardmäßig in einer Edge-Runtime, keiner vollständigen Node.js-Umgebung — viele Node-APIs (z. B. der native fs-Zugriff auf das Dateisystem, manche Datenbank-Treiber) stehen dort nicht zur Verfügung. Das ist einer der Gründe, warum Auth-Checks in proxy.ts typischerweise nur einen leichten Cookie-Vorhanden-Check machen (statt eine echte Datenbankabfrage), und die eigentliche, vollständige Session-Verifikation weiter in einer regulären Server Component oder Route Handler mit voller Node.js-Umgebung passiert.
Siehe auch: Next.js App Router, Rate Limiting, Open Redirect