Один middleware.ts в корне (рядом с app/). Edge by default: Web Request/Response, fetch, crypto.subtle — без pg, fs, Node jsonwebtoken.
Разбор
Не per-route — один файл, одна функция, matcher сужает scope. «Просто verify session в middleware» ломается: БД недоступна → stateless JWT (ok) или network round-trip на каждый request (bad).
Return value — одно из четырёх поведений:
| Return | Effect |
|---|
NextResponse.next() | Continue to the route. Attach headers/cookies here. |
NextResponse.redirect(url) | 307/308 to the client. URL changes. |
NextResponse.rewrite(url) | Serve a different route. URL stays. A/B tests, i18n, proxying. |
new Response(...) | Short-circuit entirely. Return 401/403 without rendering. |
matcher компилируется в regex на build time — статически analysable, без runtime variables. Ошибка: middleware на /_next/static/*, каждую картинку и favicon.
Итог
Middleware — Edge intercept до cache: redirect/rewrite/headers; matcher статичен, БД нет, бюджет — миллисекунды.