Next.jsMiddleTechnical
Что такое Middleware в Next.js и каковы его варианты использования?
Middleware в Next.js — Edge-функция, запускаемая до рендеринга. Используется для аутентификации, редиректов, rewrite, геолокации, A/B-тестов и rate limiting.
Middleware в Next.js
Middleware — это Edge Runtime функция, которая выполняется перед обработкой запроса, до того как сервер отдаст страницу или API-ответ. Она запускается на Edge (Vercel) или на Node.js сервере, работает с объектами NextRequest и NextResponse и может модифицировать запрос, ответ, заголовки или сделать редирект.
Структура middleware.ts
// middleware.ts (в корне проекта, рядом с app/)
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Логика здесь
return NextResponse.next();
}
// Ограничиваем, к каким маршрутам применяется Middleware
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|api/public).*)',
],
};
Вариант 1: Аутентификация и авторизация
import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose';
const SECRET = new TextEncoder().encode(process.env.JWT_SECRET!);
export async function middleware(req: NextRequest) {
const token = req.cookies.get('auth_token')?.value;
if (!token) {
return NextResponse.redirect(new URL('/login', req.url));
}
try {
await jwtVerify(token, SECRET);
return NextResponse.next();
} catch {
const res = NextResponse.redirect(new URL('/login', req.url));
res.cookies.delete('auth_token');
return res;
}
}
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*'],
};
Вариант 2: Геолокация и A/B тестирование
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest) {
// Заголовки геолокации (Vercel автоматически добавляет)
const country = req.geo?.country ?? req.headers.get('x-vercel-ip-country');
// A/B тест: 50/50 сплит
let bucket = req.cookies.get('ab_bucket')?.value;
if (!bucket) {
bucket = Math.random() < 0.5 ? 'a' : 'b';
}
const res = NextResponse.next();
res.cookies.set('ab_bucket', bucket, { maxAge: 60 * 60 * 24 * 30 });
res.headers.set('x-ab-bucket', bucket);
res.headers.set('x-country', country ?? 'unknown');
return res;
}
Вариант 3: Редиректы и rewrite
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest) {
const { pathname, search } = req.nextUrl;
// Redirect старых URL
if (pathname.startsWith('/old-blog')) {
return NextResponse.redirect(
new URL(pathname.replace('/old-blog', '/blog') + search, req.url),
{ status: 301 }
);
}
// Rewrite: показываем /maintenance без смены URL
if (process.env.MAINTENANCE_MODE === 'true') {
return NextResponse.rewrite(new URL('/maintenance', req.url));
}
return NextResponse.next();
}
Вариант 4: Rate limiting (с Redis)
import { NextRequest, NextResponse } from 'next/server';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
});
export async function middleware(req: NextRequest) {
const ip = req.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success, remaining } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: 'Too many requests' },
{ status: 429, headers: { 'X-RateLimit-Remaining': String(remaining) } }
);
}
return NextResponse.next();
}
export const config = {
matcher: '/api/:path*',
};
Ключевые ограничения Edge Runtime
- Нет доступа к файловой системе
- Нет Node.js-специфичных API (
fs,path, нативные модули) - Нет прямого подключения к PostgreSQL — только HTTP-запросы или Upstash Redis
- Лимит размера Middleware: 1 MB на Vercel
Подводные камни
- Middleware выполняется при каждом запросе: без правильного
matcherон будет запускаться для статики, шрифтов и изображений — это замедляет ответ. - Edge Runtime ограничен: нельзя использовать Prisma, pg, bcrypt и другие Node.js-зависимые пакеты напрямую.
- Чтение тела запроса недоступно:
req.bodyнедоступен в Middleware — для проверки тела используйте Route Handler. - Cookies после редиректа: при
NextResponse.redirectcookie изres.cookies.set()не всегда применяются — нужно тестировать поведение в конкретной среде деплоя. - Бесконечный редирект: если matcher перехватывает страницу /login и редирект идёт туда же — infinite loop. Всегда исключайте целевые страницы редиректа из matcher.
- Нет Promise.all для параллельных fetch: при длинной цепочке fetch в Middleware время ответа растёт линейно — держите Middleware максимально лёгким.
- Несовместимость с некоторыми npm-пакетами: пакеты, использующие
process.envво время импорта или динамические require, могут падать в Edge Runtime.
Common mistakes
- Использовать Node-only SDK в Edge middleware
- Делать тяжёлую логику и блокировать каждый запрос
- Полагаться только на middleware-проверку для авторизации
- Забывать про
matcherи обрабатывать ассеты
What the interviewer is testing
- Знает Edge vs Node runtime
- Умеет писать
matcher - Понимает ограничения API и bundle-size
- Различает middleware-проверку и RSC-проверку