Pokud v roce 2026 spravujete projekt v Next.js, pravděpodobně už jste zakopli o jednu z nejvíc diskutovaných změn šestnáctky: soubor middleware.ts se přejmenoval na proxy.ts a exportovaná funkce middleware na proxy. Na první pohled kosmetika. Ve skutečnosti je to ale dost zásadní posun v tom, jak by se měl middleware vůbec používat — a v kombinaci s loňskou bezpečnostní zranitelností CVE-2025-29927 mění to, jak bychom měli stavět autentizaci v Next.js.
Tak pojďme na to. V tomhle průvodci si projdeme kompletní migraci z middleware.ts na proxy.ts, ukážeme si funkční vzory autentizace pro App Router, vyhneme se nejčastějším pastím (kterých je víc, než byste čekali) a podíváme se na produkční doporučení od týmu Vercelu pro rok 2026.
Proč Next.js 16 přejmenoval middleware na proxy
Tým Vercelu uvedl, že název „middleware" dlouhodobě svádí vývojáře k podobnému uvažování jako u Express.js — tedy k pokušení vkládat sem byznysovou logiku, validaci JWT tokenů, dotazy do databáze a další věci, které tam vlastně nepatří. Je to v rozporu s tím, jak Next.js middleware reálně funguje. Běží před každým požadavkem na samostatném runtime a měl by zůstat extrémně lehký.
Nový název proxy má tuhle roli zpřesnit. Soubor proxy.ts je síťový hraniční bod aplikace — rozhoduje pouze o přesměrování, přepsání URL a hlavičkách. Žádné JSON odpovědi, žádná HTML těla. Pokud něco z toho potřebujete, je to docela jasný signál, že logiku máte přesunout do Server Components nebo Route Handlers.
Hlavní rozdíly oproti middleware.ts
- Runtime:
proxy.tsv Next.js 16 ve výchozím nastavení používá Node.js runtime (nikoli pouze edge). To stabilizuje přístup k Node API, ale zároveň zavádí omezení — nelze vracet těla odpovědí. - Async API: Volání
cookies(),headers()aparamsjsou nyní asynchronní a vyžadujíawait. - Konfigurační příznaky: Příznaky jako
skipMiddlewareUrlNormalizebyly přejmenovány naskipProxyUrlNormalize. - Zpětná kompatibilita:
middleware.tsstále funguje, ale je označen jako deprecated a v budoucích verzích zmizí.
Krok za krokem: Migrace z middleware.ts na proxy.ts
Krok 1: Spuštění oficiálního codemodu
Vercel naštěstí připravil codemod, který přejmenuje soubor i exportovanou funkci za vás. Spustíte ho z kořene projektu:
npx @next/codemod@canary middleware-to-proxy
Pokud chcete migrovat na Next.js 16 najednou (včetně přejmenování konfiguračních příznaků a async API), použijte hromadný upgrade codemod:
npx @next/codemod upgrade 16
Krok 2: Ruční změna funkce a exportu
Když si chcete úpravu udělat ručně (přiznám se, sám to tak občas dělám, abych viděl, co se mění), přejmenujte soubor z middleware.ts na proxy.ts a upravte export:
// Před (middleware.ts)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
return NextResponse.next()
}
// Po (proxy.ts)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
return NextResponse.next()
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}
Krok 3: Audit asynchronních API volání
Pokud váš proxy.ts pracuje s cookies nebo hlavičkami, ujistěte se, že je voláte asynchronně:
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'
export async function proxy(request: NextRequest) {
const cookieStore = await cookies()
const token = cookieStore.get('session')?.value
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
Krok 4: Ověření zápisu cookies
A teď k něčemu, na čem se dá hezky popálit: po migraci pečlivě otestujte, že zápis cookies v proxy se opravdu dostane zpět ke klientovi. Je to častý zdroj subtilních bugů, které vám codemod neodhalí — a dají se najít až tehdy, když si někdo začne stěžovat, že ho aplikace pořád odhlašuje.
CVE-2025-29927: Proč samotný middleware nestačí pro autentizaci
V březnu 2025 byla zveřejněna kritická zranitelnost CVE-2025-29927 s CVSS skóre 9.1. Útočníci mohli obejít celou middleware vrstvu pouze tím, že do požadavku přidali hlavičku x-middleware-subrequest. Tahle hlavička byla původně určená pro interní použití frameworku k prevenci nekonečných smyček — ale její přítomnost způsobila, že middleware byl prostě přeskočen.
Důsledky byly, no, dost zničující. Útočník mohl získat přístup k chráněným routám bez jakékoli autentizace. Postiženy byly všechny verze starší než 12.3.5, 13.5.9, 14.2.25 a 15.2.3.
Klíčové ponaučení pro rok 2026: nikdy se nespoléhejte pouze na middleware (proxy.ts) jako jediný bod autentizace. Středně velkým a velkým aplikacím doporučuju vzor Data Access Layer, o kterém si povíme za chvíli.
Doporučený vzor: Proxy + Data Access Layer (DAL)
Současný nejlepší vzor kombinuje rychlou kontrolu v proxy.ts s důslednou verifikací v Data Access Layer. Představte si to jako letištní bezpečnost — TSA zkontroluje letenku u vstupu (proxy), ale u gatu se kontroluje znovu (DAL). Otravné? Možná. Bezpečné? Rozhodně.
1. Proxy: rychlé hrubé filtrování
// proxy.ts
import { NextResponse, type NextRequest } from 'next/server'
const PUBLIC_ROUTES = ['/', '/login', '/register', '/about']
export async function proxy(request: NextRequest) {
const path = request.nextUrl.pathname
const isPublic = PUBLIC_ROUTES.some((route) => path === route)
if (isPublic) return NextResponse.next()
const session = request.cookies.get('session')?.value
if (!session) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}
2. Data Access Layer: skutečná autorita
DAL je vrstva, která centralizuje veškerou logiku přístupu k datům. Každá citlivá operace nejprve ověří relaci uživatele:
// lib/dal.ts
import 'server-only'
import { cookies } from 'next/headers'
import { cache } from 'react'
import { redirect } from 'next/navigation'
import { decrypt } from '@/lib/session'
export const verifySession = cache(async () => {
const cookieStore = await cookies()
const cookie = cookieStore.get('session')?.value
const session = await decrypt(cookie)
if (!session?.userId) {
redirect('/login')
}
return { isAuth: true, userId: session.userId }
})
export const getUser = cache(async () => {
const session = await verifySession()
try {
const data = await db.query.users.findFirst({
where: eq(users.id, session.userId),
columns: { id: true, name: true, email: true, role: true },
})
return data
} catch (error) {
console.error('Failed to fetch user', error)
return null
}
})
Použití cache() z Reactu deduplikuje volání během jednoho renderu, takže verifySession můžete v klidu volat z více Server Components, aniž byste se báli, že to bouchne na databázi pětkrát za sebou.
3. Server Component: konzumace DAL
// app/dashboard/page.tsx
import { getUser } from '@/lib/dal'
export default async function DashboardPage() {
const user = await getUser()
if (!user) return null
return (
<section>
<h1>Vítejte zpět, {user.name}</h1>
<p>Role: {user.role}</p>
</section>
)
}
Integrace s Auth.js v5 v Next.js 16
Používáte Auth.js v5 (dřív známé jako NextAuth.js)? Tak pozor — oficiální dokumentace ještě pořád počítá s názvem middleware.ts. V Next.js 16 musíte konfiguraci přesunout do proxy.ts a důsledně oddělit edge konfiguraci od konfigurace s databázovým adapterem:
// auth.config.ts (edge-safe, bez adapteru)
import type { NextAuthConfig } from 'next-auth'
export const authConfig = {
pages: { signIn: '/login' },
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user
const isOnDashboard = nextUrl.pathname.startsWith('/dashboard')
if (isOnDashboard) return isLoggedIn
return true
},
},
providers: [],
} satisfies NextAuthConfig
// proxy.ts
import NextAuth from 'next-auth'
import { authConfig } from './auth.config'
export default NextAuth(authConfig).auth
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}
Plnou konfiguraci s Prisma adapterem nechte v samostatném souboru auth.ts, který se používá pouze v Server Components a Route Handlers. Jinak řečeno: pokud byste auth.ts importovali do proxy, vtáhli byste Prisma do edge runtime a aplikace by spadla přesně v okamžiku, kdy by ji to nejmíň čekalo.
Nejčastější pasti při migraci
Příliš široký matcher způsobuje smyčky
Když váš matcher zachytí i přihlašovací stránku, je hotovo — vítejte v nekonečném přesměrování. Řešení: explicitně vylučte veřejné cesty buď přes matcher, nebo brzkým returnem v kódu proxy.
Statická aktiva zpomalují aplikaci
Matcher typu /:path* spustí proxy i pro každý obrázek, font a CSS. To vážně nechcete. Vždy vylučte _next/static, _next/image a běžné přípony souborů.
Validace JWT v proxy je antipattern
Verifikace tokenu pomocí jose nebo podobné knihovny je v proxy lákavá, ale pomalá — a v Next.js 16 vás navíc může zaskočit při běhu na Node.js runtime. Lehkou kontrolu existence cookie nechte v proxy, plnou verifikaci dělejte v DAL. Tečka.
Streaming autentizace pro lepší vnímaný výkon
App Router umí streamovat obsah stránky a paralelně provádět autentizační kontrolu. Místo blokování celé stránky uvidí uživatel okamžitě hlavičku a navigaci, zatímco chráněný obsah se streamuje až po dokončení kontroly:
// app/dashboard/layout.tsx
import { Suspense } from 'react'
import { Skeleton } from '@/components/skeleton'
import { UserPanel } from './_components/user-panel'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<header>Dashboard</header>
<Suspense fallback={<Skeleton />}>
<UserPanel />
</Suspense>
{children}
</div>
)
}
Podle interních měření Vercelu zlepšuje tento vzor vnímanou rychlost o 30–40 %, aniž by se zkrátil reálný čas autentizace. Z vlastní praxe můžu potvrdit, že rozdíl uživatelé skutečně vnímají — zvlášť na pomalejších mobilních sítích.
Checklist migrace pro Next.js 16
- Spusťte codemod
npx @next/codemod upgrade 16. - Přejmenujte
middleware.tsnaproxy.tsa exportmiddlewarenaproxy. - Přidejte
awaitke všem volánímcookies(),headers()aparams. - Audit konfiguračních příznaků (např.
skipMiddlewareUrlNormalize→skipProxyUrlNormalize). - Zaveďte Data Access Layer pro skutečnou autorizaci na úrovni dat.
- Otestujte matcher proti smyčkám a statickým aktivům.
- Pokud používáte Auth.js v5, rozdělte konfiguraci na edge-safe a plnou.
- Aktualizujte Next.js minimálně na 15.2.3 / 14.2.25 (kvůli CVE-2025-29927).
Často kladené dotazy
Musím v Next.js 16 přejít z middleware.ts na proxy.ts okamžitě?
Ne, middleware.ts v Next.js 16 stále funguje, ale je oficiálně označen jako deprecated. Doporučuju migrovat ještě před vydáním Next.js 17, kde bude pravděpodobně odstraněn. Codemod celou změnu provede za minutu.
Můžu v proxy.ts vracet JSON odpověď?
Ne. proxy.ts v Node.js runtime neumožňuje vracet těla odpovědí — pouze přesměrování, přepsání URL a úpravy hlaviček. Pokud potřebujete vrátit JSON, je na to Route Handler v app/api/.
Je middleware (proxy.ts) bezpečné místo pro autentizaci?
Pro hrubé filtrování ano, pro skutečnou autorizaci ne. Po zranitelnosti CVE-2025-29927 je doporučený vzor defense-in-depth: rychlá kontrola v proxy + důsledná verifikace v Data Access Layer u každé citlivé operace.
Funguje proxy.ts na edge runtime?
Ve výchozím nastavení Next.js 16 běží proxy.ts na Node.js runtime. Pokud potřebujete edge (třeba kvůli geolokaci), můžete ho explicitně vynutit pomocí export const runtime = 'edge' — ale ztratíte přístup k některým Node.js API.
Jaký je rozdíl mezi proxy.ts a Route Handlers?
proxy.ts běží před každým požadavkem a slouží k routovacím rozhodnutím (redirect, rewrite, hlavičky). Route Handlers v app/api/ jsou plnohodnotné HTTP endpointy, které vracejí těla odpovědí a zpracovávají byznysovou logiku. Obě vrstvy se doplňují, ne nahrazují.
Závěr
Přejmenování middleware.ts na proxy.ts v Next.js 16 není kosmetika. Je to signál posunu v tom, jak by framework měl přemýšlet o autentizaci a zpracování požadavků. Lehký proxy + důkladný Data Access Layer je vzor, který přežije CVE-2025-29927 i další pravděpodobné zranitelnosti — a zároveň drží vaši aplikaci rychlou. Spusťte codemod, proveďte audit cookies, zaveďte DAL a otestujte matcher. Celá migrace by neměla zabrat víc než jedno klidné odpoledne. Upřímně řečeno, viděl jsem horší upgrady.