1. Что такое middleware в Next.js и зачем он нужен
В мире веб-разработки middleware — это такой себе "швейцар на входе": он стоит перед дверью (вашим маршрутом) и решает, кого пускать, кого не пускать, а кому выдать тапочки и показать, где раздевалка. Если говорить по-серьёзному, middleware — это специальная функция, которая выполняется до того, как пользователь получит доступ к странице или API-эндпоинту, и может изменить или прервать этот доступ.
В Next.js с появлением App Router (начиная с версии 13 и выше) появилась поддержка глобального middleware через файл middleware.ts (или middleware.js). Этот файл располагается в корне вашего проекта (рядом с app/ или pages/) и автоматически применяется ко всем запросам, которые проходят через ваше приложение.
Для чего нужен middleware?
- Защита маршрутов: проверка авторизации, редирект неавторизованных пользователей на страницу входа.
- Локализация: определение языка пользователя и установка нужной локали.
- A/B тестирование: выбор варианта страницы для пользователя.
- Аналитика и логирование: сбор статистики о посещениях.
- Модификация запросов и ответов: например, установка заголовков безопасности.
middleware — это ваш первый рубеж обороны и универсальный инструмент для "глобальных" сценариев.
2. Как работает middleware в Next.js
В Next.js middleware — это обычная функция (или асинхронная функция), которая экспортируется из файла middleware.ts и принимает на вход объект request (инстанс NextRequest). Она должна вернуть объект типа NextResponse (или ничего, если вы хотите пропустить запрос дальше).
sequenceDiagram participant User participant Next.js participant Middleware participant Page/API User->>Next.js: Отправить запрос Next.js->>Middleware: Вызов (если путь совпадает) Middleware->>Middleware: Обработка запроса
(анализ, изменение, редирект, ошибка) alt Запрос обработан успешно Middleware-->>Next.js: Запрос обработан Next.js->>Page/API: Запрос к компоненту Page/API-->>Next.js: Результат else Произошел редирект Middleware-->>User: Редирект else Возникла ошибка Middleware-->>User: Ошибка end Next.js-->>User: Отправить результат
Порядок обработки запроса:
- Пользователь отправляет запрос к вашему приложению.
- Next.js вызывает middleware.ts, если путь запроса подпадает под область его действия.
- Middleware анализирует запрос, может модифицировать его, выполнить проверку, сделать редирект или даже вернуть ошибку.
- Если всё хорошо, запрос идёт дальше — к вашему компоненту страницы или API.
Создание файла middleware.ts: базовый пример
Давайте создадим свой первый middleware. Для этого в корне проекта (рядом с папкой app/ или pages/) создайте файл:
/middleware.ts
Минимальный рабочий пример:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Просто пропускаем все запросы дальше
return NextResponse.next()
}
Этот middleware ничего не делает — он просто пропускает все запросы. Но это отличная точка старта!
3. Простейшая защита маршрута
Теперь представим, что у нас есть защищённая страница /dashboard, и мы хотим, чтобы неавторизованные пользователи попадали на /login.
Для этого нам надо:
- Проверить, залогинен ли пользователь (например, по наличию cookie или заголовка).
- Если не залогинен — сделать редирект на /login.
Пример middleware с редиректом
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Проверяем, запрашивает ли пользователь защищённый маршрут
if (request.nextUrl.pathname.startsWith('/dashboard')) {
// Пример: проверяем наличие cookie "token"
const hasToken = request.cookies.has('token')
if (!hasToken) {
// Не авторизован — редирект на /login
return NextResponse.redirect(new URL('/login', request.url))
}
}
// Всё ок — пропускаем дальше
return NextResponse.next()
}
Пояснения:
Мы смотрим путь запроса через request.nextUrl.pathname.
Проверяем наличие cookie через request.cookies.has('token').
Если cookie нет — делаем редирект через NextResponse.redirect.
Если всё хорошо — пропускаем дальше через NextResponse.next().
В реальных проектах авторизация обычно сложнее (next-auth, JWT, серверная проверка), но концепция та же.
4. Область действия middleware: matcher
По умолчанию middleware применяется ко всем маршрутам приложения. Но иногда хочется, чтобы он работал только для некоторых путей (например, только для /dashboard/*, а не для /api/* или /login).
Для этого в Next.js можно использовать экспортируемый объект config и свойство matcher:
// middleware.ts
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*']
}
Теперь middleware будет применяться только к маршрутам, начинающимся с /dashboard или /profile.
Примеры шаблонов:
- /about — только к /about
- /dashboard/:path* — ко всем /dashboard и его подпутям
- /((?!api|_next/static|_next/image|favicon.ico).*) — ко всему, кроме API и служебных путей
5. Особенности работы middleware
- Только Edge Runtime
Middleware в Next.js работает на Edge Runtime — это облегчённая среда, похожая на серверless-функции, но с ограничениями. Например, нельзя использовать Node.js-модули, работать с файловой системой или запускать тяжелые вычисления. Всё должно быть максимально быстро и просто. - Только чтение cookies и заголовков
В middleware нельзя изменять cookies или заголовки "на лету", только читать их. Если нужно изменить — используйте методы NextResponse, чтобы добавить/удалить cookie в ответе. - Нет доступа к React-компонентам
Middleware работает до рендеринга React-компонентов. Здесь нельзя импортировать компоненты, обращаться к состоянию приложения или делать асинхронные запросы к базе данных (если только через fetch к внешнему API).
6. Примеры использования middleware
Пример 1. Локализация по языку браузера
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const acceptLanguage = request.headers.get('accept-language')
const userLang = acceptLanguage?.split(',')[0] || 'en'
if (request.nextUrl.pathname === '/') {
// Редиректим на /en или /ru в зависимости от языка браузера
return NextResponse.redirect(new URL(`/${userLang}`, request.url))
}
return NextResponse.next()
}
Пример 2. Установка заголовков безопасности
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-Content-Type-Options', 'nosniff')
return response
}
Пример 3. A/B тестирование
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// 50% пользователей попадают на /variant-a, 50% — на /variant-b
if (request.nextUrl.pathname === '/experiment') {
const variant = Math.random() < 0.5 ? 'a' : 'b'
return NextResponse.rewrite(new URL(`/experiment/variant-${variant}`, request.url))
}
return NextResponse.next()
}
7. Интеграция с next-auth и проверка сессии
Если вы используете next-auth, то для проверки сессии в middleware потребуется использовать специальную функцию, потому что обычные серверные функции тут недоступны.
См. официальную документацию next-auth для middleware.
Пример (очень упрощённый):
import { withAuth } from 'next-auth/middleware'
export default withAuth({
pages: {
signIn: '/login'
}
})
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*']
}
Пояснение:
withAuth автоматически проверяет сессию и редиректит на /login, если пользователь не авторизован.
Можно указать, к каким маршрутам применять middleware через matcher.
8. Типичные ошибки при работе с middleware
Ошибка №1: попытка использовать Node.js API или тяжелые библиотеки.
Middleware работает в Edge Runtime, поэтому нельзя использовать, например, fs, crypto (некоторые методы), или обращаться к базе данных напрямую. Если нужно что-то сложное — делайте это на сервере, а не в middleware.
Ошибка №2: забыли про область действия.
Если не указать matcher, middleware будет применяться ко всем маршрутам, включая API-эндпоинты и статику. Это может привести к неожиданным редиректам или ошибкам. Всегда явно указывайте, для каких путей нужен middleware.
Ошибка №3: неправильная работа с cookies.
В middleware можно только читать cookies из запроса, а чтобы добавить или удалить cookie, используйте методы NextResponse.
Ошибка №4: попытка использовать React-компоненты или серверные функции.
Middleware не может импортировать React-компоненты, обращаться к базе данных напрямую или использовать серверные функции Next.js. Всё должно быть максимально простым и быстрым.
Ошибка №5: забыли вернуть результат.
Если забыть вернуть NextResponse.next() или другой ответ, запрос "повиснет" и пользователь ничего не увидит.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ