Middleware в Next.js 15: Ръководство за автентикация и edge runtime

Middleware в Next.js 15 защитава маршрути, добавя security headers и пренасочва на edge скорост. Покрива matcher, JWT автентикация, i18n и експерименталния Node.js runtime от версия 15.2.

Middleware в Next.js 15: Гайд 2026

Актуализирано: 30 юни 2026 г.

Middleware в Next.js 15 е функция, която прихваща HTTP заявката още преди да достигне до маршрута, така че можеш да я пренапишеш, пренасочиш, да добавиш хедъри или да върнеш отговор директно. По подразбиране тя работи на Edge runtime и е първият избор за автентикация, защита на маршрути, географски пренасочвания, A/B тестове и инжектиране на security headers. От версия 15.2 насам Next.js добави и експериментална поддръжка за Node.js runtime в middleware, което отвори врата за по-сложни сценарии (например директен достъп до бази данни).

  • Middleware се дефинира в един файл middleware.ts в корена на проекта и се изпълнява преди всеки matching маршрут.
  • По подразбиране middleware работи на Edge runtime с лимит от 1 MB код и без достъп до Node.js API като fs или net.
  • Конфигурацията matcher е критична за производителността. Без нея middleware се изпълнява за всяка заявка, дори за статични файлове.
  • За автентикация използвай cookies или JWT токени, проверявай ги в middleware и пренасочвай към /login при невалидна сесия.
  • Next.js 15.2+ въвежда експериментална Node.js runtime опция чрез experimental.nodeMiddleware, давайки достъп до пълния Node API.
  • Никога не правй тежки операции в middleware. Тo се изпълнява при всяка заявка и забавянето се натрупва бързо.

Какво представлява middleware в Next.js 15?

Middleware в Next.js 15 е специален файл, който прихваща входящите HTTP заявки, преди те да достигнат до съответната страница или route handler. За разлика от Express middleware, който работи на server level и се добавя програмно, Next.js middleware се изпълнява на Vercel Edge Network (или на твоята CDN edge). Това означава, че може да реагира на заявки в най-близката до потребителя географска локация, често за под 50 ms.

Основните случаи на употреба включват: проверка на автентикационни cookies преди показване на защитена страница, пренасочване на потребители според държава или език, добавяне на security headers като Content-Security-Policy, реализация на A/B тестове чрез презаписване на URL-и, блокиране на ботове и rate limiting. Тъй като middleware работи преди React да започне рендерирането, той е идеалното място за решения, които трябва да се вземат на ниво заявка, преди да се породят Server Components.

Важно е да се разбере, че middleware не е заместител на route handlers или Server Actions. Не трябва да обработва бизнес логика, тежки заявки към база данни или сложни изчисления. Ако имаш нужда от мутации и валидация на форми, разгледай нашето ръководство за Server Actions в Next.js 15, което покрива тези сценарии в дълбочина.

Основна настройка и структура на файла

Middleware в Next.js 15 се дефинира в един единствен файл с име middleware.ts (или middleware.js) в корена на проекта, на същото ниво като app/ или src/app/. Файлът трябва да експортира функция с име middleware (default или named export):

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Добавяме custom header към всяка заявка
  const response = NextResponse.next()
  response.headers.set('x-custom-header', 'hello-from-middleware')
  return response
}

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

Обектът NextRequest разширява стандартния Web Request с допълнителни помощни методи: request.cookies.get(), request.nextUrl (parsed URL с pathname, searchParams), request.geo (държава, град, само на Vercel) и request.ip. NextResponse добавя статични методи като NextResponse.redirect(), NextResponse.rewrite(), NextResponse.json() и NextResponse.next() за продължаване на pipeline-а с модификации.

Как да конфигурирам matcher правилно?

Конфигурацията matcher определя кои URL пътища ще задействат middleware. Без правилно настроен matcher, middleware се изпълнява за всяка заявка, включително /favicon.ico, /_next/static/* и API endpoints. Това може да доведе до сериозно влошаване на производителността и излишни Edge function invocations (които струват пари на Vercel).

Лично аз изгорях по този начин в първия си production проект с App Router. Edge invocations скочиха 10x за един ден, защото matcher-ът беше прекалено широк и хващаше и image optimization заявките. Препоръчителният подход е да използваш negative regex pattern, който изключва статичните asset-и:

export const config = {
  matcher: [
    /*
     * Match всички пътища с изключение на:
     * - _next/static (статични файлове)
     * - _next/image (image optimization)
     * - favicon.ico, robots.txt, sitemap.xml
     * - всички файлове с разширение (.png, .jpg, и т.н.)
     */
    '/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|.*\\..*).*)',
  ],
}

За по-голяма гъвкавост, matcher може да бъде масив от обекти с условни проверки:

export const config = {
  matcher: [
    {
      source: '/dashboard/:path*',
      has: [{ type: 'cookie', key: 'session' }],
    },
    {
      source: '/admin/:path*',
      missing: [{ type: 'header', key: 'x-admin-token' }],
    },
  ],
}

Поддръжката за has и missing ти позволява да филтрираш по cookies, headers, query params и host, преди дори middleware функцията да се изпълни. Това е значително по-производително от ръчни проверки вътре в тялото на функцията.

Автентикация и защита на маршрути с middleware

Най-честият случай на употреба на middleware е защитата на маршрути от неавтентикирани потребители. Типичният поток включва: четене на session cookie, валидиране на JWT или сесиен токен, и пренасочване към /login, ако токенът липсва или е невалиден.

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { jwtVerify } from 'jose'

const PROTECTED_PATHS = ['/dashboard', '/account', '/admin']
const SECRET = new TextEncoder().encode(process.env.JWT_SECRET!)

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl
  const isProtected = PROTECTED_PATHS.some((p) => pathname.startsWith(p))

  if (!isProtected) return NextResponse.next()

  const token = request.cookies.get('session')?.value
  if (!token) {
    const loginUrl = new URL('/login', request.url)
    loginUrl.searchParams.set('from', pathname)
    return NextResponse.redirect(loginUrl)
  }

  try {
    const { payload } = await jwtVerify(token, SECRET)
    // Подаваме user ID към downstream чрез header
    const headers = new Headers(request.headers)
    headers.set('x-user-id', payload.sub as string)
    return NextResponse.next({ request: { headers } })
  } catch {
    // Невалиден или изтекъл токен
    return NextResponse.redirect(new URL('/login', request.url))
  }
}

export const config = {
  matcher: ['/dashboard/:path*', '/account/:path*', '/admin/:path*'],
}

Този pattern е edge-safe, защото библиотеката jose работи в Edge runtime благодарение на това, че използва Web Crypto API вместо Node.js crypto модула. jsonwebtoken и подобни Node-only библиотеки няма да работят в Edge middleware. Знам го от личен опит, защото си загубих два часа в дебъгване, преди да осъзная защо build-ът се чупи.

Пренасочвания, презаписвания и хедъри

Middleware ти дава три основни инструмента за модификация на заявката:

МетодКакво правиURL в браузъраТипичен случай
NextResponse.redirect()Връща 307/308 и кара браузъра да направи нова заявкаСменя сеLogin redirect, legacy URLs
NextResponse.rewrite()Сменя вътрешно URL-а, но запазва оригиналния в браузъраОстава същиятA/B тестове, i18n, multi-tenant
NextResponse.next()Продължава към оригиналния маршрутОстава същиятДобавяне на headers, logging
NextResponse.json()Връща JSON отговор директно, прекъсвайки pipeline-аОстава същиятBot блокиране, rate limit отговори

Ето пример за добавяне на security headers към всички страници:

export function middleware(request: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('X-Frame-Options', 'DENY')
  response.headers.set('X-Content-Type-Options', 'nosniff')
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
  response.headers.set(
    'Strict-Transport-Security',
    'max-age=63072000; includeSubDomains; preload'
  )
  return response
}

За динамичен CSP с nonce (необходим за inline scripts в App Router), генерирай nonce-а в middleware и го подай чрез request header. Server Components могат да го прочетат с headers() функцията. За справка какво точно покрива всеки header, разгледай MDN документацията за HTTP headers.

Edge runtime срещу Node.js runtime в middleware

Исторически middleware в Next.js работеше изключително на Edge runtime, ограничена среда, базирана на V8 isolates, без достъп до Node.js специфични API. Това гарантираше ниска латентност, но в същото време ограничаваше възможностите. Версия 15.2 въведе експериментална поддръжка за Node.js runtime в middleware:

// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  experimental: {
    nodeMiddleware: true,
  },
}

export default config
// middleware.ts
export const config = {
  runtime: 'nodejs', // вместо 'edge' (по подразбиране)
  matcher: '/api/:path*',
}

Кога да избереш Node.js runtime? Когато имаш нужда от пълен достъп до npm пакети, които използват Node API (например Prisma client директно, sharp за image processing, или native crypto модула), по-голям code size лимит, и възможност за свързване с self-hosted бази данни без edge-compatible drivers. За database access от Server Components, разгледай нашето ръководство за Drizzle ORM в Next.js 15.

Компромисът е реален: Node middleware има по-висок cold start и не може да работи на global edge network. За масови приложения с потребители по целия свят, Edge остава предпочитаният избор, дори ако означава пренаписване на някои библиотеки за compatibility.

Интернационализация (i18n) с middleware

Next.js 15 премахна вградената i18n routing функционалност от App Router (тя остана само в Pages Router), което прави middleware задължителен инструмент за многоезични сайтове. Типичният pattern включва детекция на език от Accept-Language header или cookie, и пренасочване към съответната езикова версия.

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const LOCALES = ['bg', 'en', 'de'] as const
const DEFAULT_LOCALE = 'bg'

function detectLocale(request: NextRequest): string {
  // 1. Проверяваме cookie
  const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value
  if (cookieLocale && LOCALES.includes(cookieLocale as any)) return cookieLocale

  // 2. Проверяваме Accept-Language header
  const accept = request.headers.get('accept-language') ?? ''
  const preferred = accept.split(',')[0]?.split('-')[0]
  if (LOCALES.includes(preferred as any)) return preferred

  return DEFAULT_LOCALE
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl
  const hasLocale = LOCALES.some(
    (l) => pathname.startsWith(`/${l}/`) || pathname === `/${l}`
  )

  if (hasLocale) return NextResponse.next()

  const locale = detectLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  return NextResponse.redirect(request.nextUrl)
}

export const config = {
  matcher: '/((?!_next|api|.*\\..*).*)',
}

Този подход работи добре заедно с библиотеки като next-intl или next-international, които очакват [locale] dynamic segment в App Router структурата.

Защо моят middleware не се изпълнява?

Това е една от най-честите грешки, които разработчиците срещат. Ето кратък контролен списък:

  1. Локация на файла: middleware.ts трябва да е в корена на проекта (до app/) или вътре в src/, ако използваш src/ структура. Не вътре в app/!
  2. Грешен export: функцията трябва да се казва middleware или да е export default. Произволни имена не работят.
  3. Matcher изключва пътя ти: ако твоят matcher е /api/:path*, middleware няма да се изпълни за /dashboard. Провери matcher pattern.
  4. Кеширан build: понякога Next.js dev сървърът кешира middleware конфигурации. Рестартирай с rm -rf .next && pnpm dev.
  5. Async без await: ако middleware-ът ти е async и забравиш await, request-ът може да продължи преди логиката да завърши.
  6. Грешка в Edge compatibility: ако импортираш Node-only модул, middleware ще се счупи безшумно при build. Провери логовете за module not found или Dynamic Code Evaluation warnings.

За проследяване на изпълнението, добави console.log в началото на функцията. В development логовете се появяват в терминала, а в production (Vercel) в Edge function logs. Ако не виждаш никакъв output, middleware определено не се задейства за този маршрут.

Най-добри практики и производителност

Middleware се изпълнява преди всяко рендериране, така че всяка милисекунда забавяне се отразява на TTFB (Time To First Byte) за цялото приложение. Ето принципите, които следваме в продукция:

  • Винаги имай matcher: Без matcher, middleware се изпълнява за статични файлове, image optimization заявки и всеки asset. Това е скъпо и бавно.
  • Избягвай мрежови заявки: Не прави fetch към външни услуги в middleware, освен ако не е абсолютно необходимо. Кеширай резултатите в cookies или edge KV.
  • Минимизирай размера на bundle: Edge runtime има 1 MB лимит. Импортирай само това, което ти трябва. Избягвай големи библиотеки като moment.js или lodash (използвай date-fns и lodash-es с tree shaking).
  • Един middleware, много responsibilities: Next.js поддържа само ЕДИН middleware файл. Организирай логиката във функции и ги композирай. Не се опитвай да добавиш втори middleware.
  • Тествай локално и на edge: Поведението в development може да се различава от production edge environment. Тествай върху Vercel preview deployments, преди да пуснеш в продукция.
  • Координирай с кеширане: Middleware може да повлияе на кеширането на Next.js (например чрез добавяне на Vary headers). Прегледай нашето ръководство за кеширане в Next.js 15 за пълната картина.

За production-grade автентикация препоръчваме комбинация от middleware за бърза JWT проверка и Server Component layer за пълна сесийна валидация. Това дава най-доброто от двата свята: ниска латентност на edge и сигурност от server-side проверка.

Често задавани въпроси

Каква е разликата между middleware и API routes в Next.js?

Middleware се изпълнява преди заявката да достигне до маршрут и обикновено модифицира или пренасочва заявката, без да връща съдържание. API routes (route handlers в App Router) са крайни endpoints, които обработват бизнес логика и връщат данни. Middleware работи на Edge runtime по подразбиране, докато route handlers могат да изберат Edge или Node runtime.

Може ли middleware да достъпва базата данни?

На Edge runtime само ако използваш edge-compatible driver (например Neon serverless, Upstash Redis, или Drizzle с HTTP driver). На Node.js runtime (експериментално в 15.2+) може да използваш всеки Node-compatible driver, включително Prisma. Все пак избягвай тежки заявки в middleware, защото те се изпълняват при всяка заявка и забавят TTFB.

Колко middleware файла мога да имам в Next.js проект?

Само един. Next.js поддържа точно един middleware.ts файл в корена на проекта (или в src/). За множество отговорности, организирай кода в отделни функции и ги извиквай последователно от главния middleware. Това е дизайнерско решение за гарантиране на предвидим pipeline на изпълнение.

Как да тествам middleware локално?

В pnpm dev или npm run dev middleware работи автоматично. Добави console.log в началото на функцията и наблюдавай терминала. За unit тестове, импортирай функцията и подай mock NextRequest обект. За пълни E2E тестове, използвай Playwright или Cypress срещу next dev или Vercel preview deployment.

Защо моят middleware се изпълнява за статични файлове?

Защото matcher конфигурацията ти е твърде широка или липсва. Без matcher, middleware прихваща ВСЯКА заявка, включително /_next/static/*, /favicon.ico и image optimization endpoints. Добави negative regex matcher като '/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)', за да изключиш статичните asset-и.

Editorial Team
За Автора Editorial Team

Our team of expert writers and editors.