IlmHamroh
JavaScript Full-stack/23-qism. Next.js26/43-dars21 daqiqa
Mundarija (35)

Next.js proxy.ts: so'rov sahifaga yetmasdan — matcher, rewrite va redirect

Qisqacha: proxy.ts — so'rov sahifaga yoki Route Handler'ga yetmasdan oldin ishlaydigan bitta funksiya. U so'rovni boshqa sahifaga yo'naltiradi (redirect), URL'ni o'zgartirmay boshqa sahifani ko'rsatadi (rewrite), sarlavha va cookie qo'shadi. Next 16 da middleware.ts shu nomga o'zgardi va sukut bo'yicha Node'da ishlaydi. matcher — proxy qaysi yo'llarda ishlashini belgilaydi; uni keng yozsangiz, rasm va CSS so'rovlari ham proxy'dan o'tib, buzilishi mumkin.

Bu darsda

  • proxy.ts so'rov yo'lida qayerda turishini va nega u "eshik oldidagi qo'riqchi" ekanini tushuntirasiz.
  • NextResponse.next(), rewrite() va redirect() farqini haqiqiy javoblar bilan ajrata olasiz.
  • matcher yozasiz va uning tuzog'ini ko'rasiz: kanondagi matcher .png rasmni ham ushlagan edi.
  • portfolio dagi Maxsus fayllar darsidan qolgan muammoni — noma'lum loyiha birinchi so'rovda 200 qaytarishini — proxy bilan tuzatasiz.
  • Proxy'da nima qilmaslik kerakligini bilasiz: to'liq auth tekshiruvi va sekin so'rovlar.

Oldin bilishingiz kerak: Route Handlers, Maxsus fayllar: not-found, HTTP sarlavhalari va cookie, Regex asoslari.

1. Nega bu kerak?

Maxsus fayllar darsida kanon g'alati narsani o'lchagan edi. /loyihalar/yoq2 — bunday loyiha yo'q. Sahifa notFound() chaqiradi, ekranda «Sahifa topilmadi» chiqadi. Lekin status:

text
yoq2 #1 200 0.181797   (birinchi so'rov: PPR qobig'i oqim bilan ketdi → 200, HTML'da <meta name="robots" content="noindex"/> va "Sahifa topilmadi")
yoq2 #2 404 0.047858   (keyingi so'rovlar: natija keshlangan → 404)
yoq2 #3 404 0.011140
yoq3 #1 200            (har yangi noma'lum slug'da birinchi so'rov yana 200)

Bu natijalar/tajriba/06-notfound-status.txt dan. Har yangi noma'lum manzilning birinchi so'rovi 200 qaytaradi. Sabab: Cache Components yoqilgan loyihada sahifaning statik qobig'i darhol oqim bilan jo'natiladi — status 200 bilan. notFound() esa keyinroq, oqim ichida ishlaydi. Status allaqachon ketgan, uni qaytarib bo'lmaydi.

noindex bor, shuning uchun Google sahifani indeksga olmaydi. Lekin 200 — yolg'on javob: monitoring vositalari, havola tekshiruvchilar va boshqa dasturlar "sahifa bor" deb o'ylaydi. Oddiy yechim — dynamicParams = false — bu loyihada taqiqlangan:

text
# B: export const dynamicParams = false — BUILD XATO: Route segment config "dynamicParams" is not compatible with `nextConfig.cacheComponents`.

Kerak bo'lgan narsa — so'rovni sahifa render bo'lishidan oldin tekshirish. Slug ro'yxatda yo'q bo'lsa, sahifani umuman boshlamaslik. Bu aynan proxy.ts ning ishi.

Hayotiy o'xshatish: bino eshigidagi qo'riqchi. U har bir kiruvchini to'xtatadi, ro'yxatga qaraydi va uch ishdan birini qiladi: o'tkazadi, boshqa eshikka yo'naltiradi yoki boshqa xonaga olib kiradi. Lekin qo'riqchi kabinetlar ichidagi ishni qilmaydi — faqat eshikda turadi.

2. Proxy so'rov yo'lida

2.1 Bitta fayl, bitta funksiya

proxy.ts loyiha ildizida yoki src/ ichida turadi (app/ bilan bir darajada). Loyihada faqat bitta proxy fayli bo'ladi. Ichida — proxy nomli funksiya va ixtiyoriy config:

ts
import { type NextRequest, NextResponse } from "next/server";

export function proxy(request: NextRequest) {
  console.log("so'rov:", request.nextUrl.pathname);
  return NextResponse.next();
}

export const config = {
  matcher: "/loyihalar/:slug",
};
Qism Nima
NextRequest Web standart Request + qulayliklar: nextUrl, cookies
NextResponse.next() "o'tkazib yubor" — so'rov odatdagidek sahifaga boradi
config.matcher proxy faqat shu yo'llarda ishlaydi

NextRequest va NextResponse — Route Handlers darsidan tanish. Farqi shundaki, Route Handler javob yaratadi, proxy esa ko'pincha so'rovni yo'naltiradi.

2.2 Ishlash tartibi

Next hujjati so'rov qaysi tartibda ishlanishini aniq yozgan. Soddalashtirilgan ko'rinishi:

flowchart TB
  S["Brauzer so'rovi"] --> H["next.config: headers, redirects"]
  H --> P["proxy.ts"]
  P --> F["Fayllar: public/, _next/static, app/"]
  F --> D["Dinamik marshrut: /loyihalar/[slug]"]
  D --> J["Javob"]

Ikki xulosa. Birinchisi: oddiy, o'zgarmas yo'naltirish (/eski → /yangi) uchun proxy kerak emas — next.config dagi redirects undan oldin ishlaydi va soddaroq (next.config chuqur). Ikkinchisi: proxy fayllardan oldin turadi. Ya'ni matcher bo'lmasa, u har bir so'rovda ishlaydi — CSS, JS, rasm, favicon ham.

2.3 Node runtime va nom o'zgarishi

Next 15 gacha bu fayl middleware.ts deb atalardi va sukut bo'yicha Edge muhitida (cheklangan, Node API'larisiz yengil muhit) ishlardi. Node'da ishlatish imkoni 15.2 da tajriba sifatida, 15.5 da barqaror bo'lib qo'shilgan edi. Next 16 da ikki o'zgarish bo'ldi:

Next 15 Next 16
Fayl middleware.ts proxy.ts
Funksiya middleware proxy
Runtime sukut — Edge (15.5 dan Node ham) faqat Node; runtime sozlamasi xato beradi

Eski middleware.ts Next 16.4 da hali ishlaydi, lekin eskirgan (deprecated): next dev va next build har safar The "middleware" file convention is deprecated. Please use "proxy" instead. deb ogohlantiradi.

Hujjat nom o'zgarishini shunday tushuntiradi: "middleware" so'zi Express'dagi middleware bilan adashtirilardi. "Proxy" esa aniqroq — ilova oldidagi tarmoq chegarasi. Hujjat yana bir narsani ochiq aytadi: bu imkoniyatni "oxirgi chora" sifatida ishlatish tavsiya etiladi.

Eski loyihani ko'chirish uchun rasmiy codemod bor (fayl va funksiya nomini almashtiradi):

bash
npx @next/codemod@canary middleware-to-proxy .

Node runtime'ning foydasi portfolio da darhol ko'rinadi: proxy fayl tizimini o'qiy oladi (node:fs/promises). Edge'da bu mumkin emas edi. Edge'ga qaytishga urinish esa xato beradi — kanon tajribasi (Runtime'lar):

text
Proxy does not support Edge runtime

Tekshirib ko'ring: Loyihada src/app/ bor. proxy.ts ni qayerga qo'yasiz: src/proxy.ts, src/app/proxy.ts yoki loyiha ildiziga?

Javob

src/proxy.ts. Qoida: proxy app/ bilan bir darajada turadi. src/ ishlatilsa — src/ ichida, aks holda — loyiha ildizida. app/ ichiga qo'yilgan proxy.ts oddiy fayl bo'lib qoladi va hech qachon ishlamaydi — xato ham bermaydi, shuning uchun bu tuzoq.

3. Uch javob: next, rewrite, redirect

3.1 Farqi

Proxy funksiyasi uchta asosiy javobdan birini qaytaradi:

Javob Brauzer manzili Kim javob beradi O'xshatish
NextResponse.next() o'zgarmaydi so'ralgan sahifa qo'riqchi o'tkazdi
NextResponse.rewrite(url) o'zgarmaydi boshqa sahifa boshqa xonaga olib kirdi
NextResponse.redirect(url) o'zgaradi (307/308) brauzer yangi so'rov yuboradi "u eshikdan kiring" dedi

Rewrite (qayta yozish) — foydalanuvchi /loyihalar/yoq2 ni ko'rib turadi, server esa ichkarida boshqa yo'lni chizadi. Redirect (yo'naltirish) — server "boshqa manzilga bor" deydi, brauzer manzil satri o'zgaradi va ikkinchi so'rov ketadi.

Bundan tashqari proxy javobni o'zi ham qaytara oladi: Response.json({ xato: "..." }, { status: 401 }). Bu API yo'llarida uchraydi.

3.2 portfolio uchun qaysi biri?

Noma'lum slug uchun kerak bo'lgan narsa: status 404, manzil o'zgarmasin (foydalanuvchi qaysi manzilni xato yozganini ko'rsin), sahifa — bizning chiroyli 404. Redirect /404 ga olib borsa, manzil o'zgarardi va status avval 307 bo'lardi. Demak — rewrite.

Rewrite qayerga? Kanon aqlli yo'l tanladi: mavjud bo'lmagan ichki yo'lga — /_topilmadi. Bunday sahifa yo'q, shuning uchun Next root not-found.tsx ni 404 status bilan ko'rsatadi:

ts
return NextResponse.rewrite(new URL("/_topilmadi", request.url));

_ bilan boshlangan papka nomi app/ da private papka — u hech qachon marshrut bo'lmaydi. Demak /_topilmadi ni hech kim tasodifan yaratib qo'ymaydi.

Tekshirib ko'ring: Foydalanuvchi /loyihalar/vazifalr (xato bilan) yozdi. Proxy redirect bilan /404 ga yuborsa va rewrite bilan /_topilmadi ni ko'rsatsa — brauzer manzil satrida nima ko'rinadi?

Javob

Redirect'da — /404: foydalanuvchi qayerda xato qilganini endi ko'rmaydi, «Orqaga» tugmasi ham chalkashadi. Rewrite'da — /loyihalar/vazifalr: manzil o'zgarmagan, xatoni ko'rib tuzatish oson. Status ham to'g'ri — 404 (redirect'da birinchi javob 307 bo'lardi).

4. matcher — proxy qayerda ishlaydi

4.1 Sintaksis

matcher — satr yoki satrlar massivi. Har satr — yo'l shabloni:

Shablon Mos keladi Mos kelmaydi
/haqimda /haqimda /haqimda-2
/loyihalar/:slug /loyihalar/bahor /loyihalar/bahor/rasm
/loyihalar/:yol* /loyihalar, /loyihalar/a/b /blog
/blog/:slug([a-z0-9-]+) /blog/portfolio-v2 /blog/rss.xml

:slug — nomli parametr, bitta segment. * — nol yoki ko'p segment. Qavs ichida — regex: parametr qanday belgilardan iborat bo'lishi mumkin. matcher qiymatlari build vaqtida o'qiladi, shuning uchun ular o'zgarmas bo'lishi shart: o'zgaruvchidan yasalgan satrni Next e'tiborsiz qoldiradi.

matcher bo'lmasa — proxy hamma narsada ishlaydi. Hujjat ogohlantiradi: shunda auth yoki redirect mantig'i CSS, JS va rasmlarni ham to'sib qo'yishi mumkin. Shuning uchun keng tarqalgan "inkor" shabloni bor:

ts
export const config = {
  matcher: ["/((?!api|_next/static|_next/image|.*\\.png$).*)"],
};

(?!...) — "bu bilan boshlanmasa" (negative lookahead). Ya'ni: api, _next/static, _next/image va .png bilan tugaydiganlardan tashqari hamma yo'l.

4.2 Biz topdik: matcher rasmni ham ushladi

Kanondagi birinchi matcher shunday edi:

ts
export const config = {
  matcher: [
    "/loyihalar/:slug",
    "/loyihalar/:slug/rasm",
    "/blog/:slug((?!rss\\.xml$).*)",
  ],
};

Mantiqan to'g'ri ko'rinadi: loyiha sahifasi, rasm sahifasi va blog posti (RSS'dan tashqari). O'lchovlar o'tdi, 404 tuzaldi. Lekin besh holatdan keyin, next/image darsida telefon o'lchovi g'alati natija berdi: mobil ekranda loyiha rasmi umuman ko'rinmagan.

Sabab — public/loyihalar/vazifalar.png. Uning manzili /loyihalar/vazifalar.png. :slug esa "bitta segment, istalgan belgi" degani — vazifalar.png ham bitta segment! Proxy uni loyiha slug'i deb o'yladi, ro'yxatdan topmadi va 404 ga burdi.

Biz buni Next'ning o'z sinov yordamchisi bilan tekshirdik (next/experimental/testing/server, biz ishga tushirdik, next 16.4.0):

text
/loyihalar/vazifalar        eski: true  yangi: true
/loyihalar/yoq              eski: true  yangi: true
/loyihalar/vazifalar/rasm   eski: true  yangi: true
/loyihalar/vazifalar.png    eski: true  yangi: false
/blog/portfolio-v2          eski: true  yangi: true
/blog/rss.xml               eski: false  yangi: false
/                           eski: false  yangi: false
/haqimda                    eski: false  yangi: false

true — proxy shu yo'lda ishlaydi. To'rtinchi qatorga qarang: eski matcher rasmni ushlaydi, yangisi — yo'q. Tuzatilgan matcher slug'ga qaysi belgilar ruxsatligini aytadi:

ts
export const config = {
  matcher: [
    "/loyihalar/:slug([a-z0-9-]+)",
    "/loyihalar/:slug([a-z0-9-]+)/rasm",
    "/blog/:slug([a-z0-9-]+)",
  ],
};

Kichik harf, raqam va chiziqcha — slug'larimiz aynan shunday. Nuqtali yo'l (.png, .xml) endi o'tmaydi, shuning uchun RSS uchun alohida istisno ham kerak emas.

Nega bu xato besh holat davomida bilinmadi? Desktop o'lchamdagi rasm variantlari eski build'lardan keshda qolgan edi. Faqat kesh tozalanib, telefon o'lchami so'ralganda xato chiqdi. Bu voqeaning to'liq tafsiloti — next/image darsida.

Maslahat: Hujjatda (16.4.0 bilan keladigan proxy.md ham) bu yordamchining nomi unstable_doesProxyMatch deb yozilgan. Lekin next 16.4.0 paketida faqat eski nom bor — unstable_doesMiddlewareMatch (biz paketdagi next/experimental/testing/server eksportlarini tekshirdik). Hujjat va paket farq qilsa — paket ustun: kodda unstable_doesMiddlewareMatch ni yozing. unstable_ prefiksi — API hali barqaror emas, nomi o'zgarishi mumkin degani.

4.3 Natija oynasida: matcher modeli

Next'ning matcher i ichkarida :slug ni regex'ga aylantiradi. G'oyani kichik model bilan ko'rsatamiz. :nom → "slash'gacha istalgan belgi", :nom(regex) → shu regex. Bu Next kodi emas — soddalashtirilgan o'quv modeli, lekin natijasi yuqoridagi haqiqiy jadval bilan bir xil:

js
function shablonniRegex(shablon) {
  const ichi = shablon.replace(
    /:(\w+)(\(([^)]*)\))?/g,
    (_, nom, __, re) => `(${re ?? "[^/]+"})`,
  );
  return new RegExp(`^${ichi}$`);
}

const eski = shablonniRegex("/loyihalar/:slug");
const yangi = shablonniRegex("/loyihalar/:slug([a-z0-9-]+)");

const yollar = ["/loyihalar/vazifalar", "/loyihalar/vazifalar.png"];
for (const yol of yollar) {
  console.log(yol, eski.test(yol), yangi.test(yol));
}
console.log(eski.source);
console.log(yangi.source);

Konsolda:

text
/loyihalar/vazifalar true true
/loyihalar/vazifalar.png true false
^\/loyihalar\/([^/]+)$
^\/loyihalar\/([a-z0-9-]+)$

[^/]+ — "slash bo'lmagan har qanday belgi, bir yoki ko'p". Nuqta ham shu to'plamga kiradi. [a-z0-9-]+ esa nuqtani o'tkazmaydi. Oxirgi ikki qator — model yasagan regex'lar (\/ — slash'ning qochirilgan ko'rinishi, ma'nosi o'sha).

5. portfolio ning proxy'si

5.1 Kod

Kanondagi src/proxy.ts (holat 27):

ts
import { readdir } from "node:fs/promises";
import path from "node:path";
import { type NextRequest, NextResponse } from "next/server";
import { LOYIHALAR } from "@/malumot/loyihalar";

const LOYIHA_SLUGLARI = new Set(LOYIHALAR.map((l) => l.slug));
let postSluglari: Promise<Set<string>> | undefined;

function postlarniOl(): Promise<Set<string>> {
  postSluglari ??= readdir(path.join(process.cwd(), "src", "blog")).then(
    (f) =>
      new Set(f.filter((x) => x.endsWith(".md")).map((x) => x.slice(0, -3))),
  );
  return postSluglari;
}

export async function proxy(request: NextRequest) {
  const [, bolim, slug] = request.nextUrl.pathname.split("/");
  const bormi =
    bolim === "loyihalar"
      ? LOYIHA_SLUGLARI.has(slug ?? "")
      : (await postlarniOl()).has(slug ?? "");
  if (bormi) return NextResponse.next();
  return NextResponse.rewrite(new URL("/_topilmadi", request.url));
}

Pastida — «Biz topdik» bo'limidagi tuzatilgan config.matcher. Kanon faylidagi uzun izohlar bu yerda olib tashlandi; kod qatorlari aynan (ikkitasi 70 belgidan uzun — prettier formati).

Qatorma-qator:

Qator Nima qiladi
LOYIHA_SLUGLARI loyihalar ro'yxatidan Set — tez has() tekshiruvi
postSluglari ??= … blog papkasini bir marta o'qiydi, Promise'ni eslab qoladi
split("/") /loyihalar/bahor/rasm → ["", "loyihalar", "bahor", "rasm"]
bormi bo'lim bo'yicha tegishli to'plamdan qidiradi
rewrite("/_topilmadi") yo'q bo'lsa — 404 sahifa, manzil o'zgarmaydi

??= — "faqat bo'sh bo'lsa yoz" (mantiqiy tayinlash). Birinchi so'rov readdir ni boshlaydi, keyingilari tayyor Promise'ni oladi. Modul darajasidagi o'zgaruvchi server jarayoni yashaguncha saqlanadi. Proxy har so'rovda ishlaydi — har safar diskni o'qish sekin bo'lardi.

5.2 Natija oynasida: proxy qarori

Proxy'ning qaror qismi — toza mantiq: yo'l kiradi, qaror chiqadi. Uni Next'siz sinash mumkin (fayl o'qish o'rniga tayyor to'plam):

js
const LOYIHALAR = new Set(["vazifalar", "bahor", "landing"]);
const POSTLAR = new Set(["portfolio-v2", "server-components"]);

function qaror(yol) {
  const [, bolim, slug] = yol.split("/");
  const toplam = bolim === "loyihalar" ? LOYIHALAR : POSTLAR;
  return toplam.has(slug ?? "") ? "next()" : "rewrite /_topilmadi";
}

for (const yol of [
  "/loyihalar/bahor",
  "/loyihalar/yoq2",
  "/loyihalar/bahor/rasm",
  "/blog/portfolio-v2",
  "/blog/yoq",
]) {
  console.log(`${yol} → ${qaror(yol)}`);
}

Konsolda:

text
/loyihalar/bahor → next()
/loyihalar/yoq2 → rewrite /_topilmadi
/loyihalar/bahor/rasm → next()
/blog/portfolio-v2 → next()
/blog/yoq → rewrite /_topilmadi

/loyihalar/bahor/rasm ham to'g'ri o'tdi: split ning uchinchi elementi baribir bahor. Bu funksiya proxy'ga faqat matcher o'tkazgan yo'llarda chaqiriladi — shuning uchun /haqimda kabi yo'llarni o'ylash shart emas.

5.3 Haqiqiy natija

Build jadvalining oxirida yangi qator paydo bo'ldi (natijalar/tekshiruv-27.txt):

text
ƒ Proxy (Middleware)

Qavs ichidagi eski nom — Next 16.4 build chiqishi hali shunday yozadi. Sahifalar o'zgarmadi: hammasi avvalgidek ○ va ◐.

next start ustida curl bilan tekshiruv (natijalar/27-proxy-status.txt, birinchi qatorlar):

text
/loyihalar/yoq2 404 0.098980s <meta name="robots" content="noindex"/> Sahifa topilmadi 
/loyihalar/yoq2 404 0.029150s <meta name="robots" content="noindex"/> Sahifa topilmadi 
/loyihalar/yoq3/rasm 404 0.010865s <meta name="robots" content="noindex"/> Sahifa topilmadi 
/blog/yoq 404 0.013855s <meta name="robots" content="noindex"/> Sahifa topilmadi 
/blog/rss.xml 200 0.053749s 

yoq2 endi birinchi so'rovdayoq 404. Mavjud sahifalar va RSS — 200. Farq qayerdan kelganini qadamma-qadam solishtiring:

Qadam Proxy'siz Proxy bilan
1. /loyihalar/yoq2 so'rovi keldi sahifa render boshlanadi proxy slug'ni to'plamdan qidiradi
2. Javob boshlanadi qobiq 200 bilan oqimda ketadi topilmadi → rewrite /_topilmadi
3. notFound() oqim ichida — status allaqachon ketgan ishlamaydi ham: sahifa boshlanmadi
Natija 200 + noindex 404 + o'sha «Sahifa topilmadi»

Narxi-chi? Proxy har mos so'rovda qo'shimcha kod. Kanon 6 tadan so'rov yubordi (soniyalarda, birinchisi — sovuq start):

text
0.037377 0.007037 0.007110 0.006864 0.009127 0.006920  ← / (proxy bilan)
0.058973 0.023385 0.014158 0.010083 0.011659 0.012055  ← /loyihalar/vazifalar (proxy bilan)
0.091689 0.010709 0.007126 0.006982 0.006199 0.006007  ← / (proxy'siz)
0.070359 0.006713 0.010841 0.010894 0.008548 0.007893  ← /loyihalar/vazifalar (proxy'siz)

/loyihalar/vazifalar proxy bilan 10–23 ms, proxy'siz 7–11 ms. / — matcher'ga kirmaydi, farqi yo'q. Ya'ni bir necha millisekund — o'lchov shovqini darajasida. Lekin bu tez, chunki proxy faqat xotiradagi to'plamni tekshiradi. Ichida tarmoq so'rovi bo'lsa, har sahifa shuncha kutardi.

6. Proxy'ning boshqa ishlari

Proxy so'rovga ham, javobga ham sarlavha qo'sha oladi. Cookie'lar — oddiy sarlavha (Cookie va Set-Cookie), Next ularga qulay cookies obyektini beradi. Hujjatdagi naqsh:

ts
export function proxy(request: NextRequest) {
  const tema = request.cookies.get("tema")?.value;
  const sarlavhalar = new Headers(request.headers);
  sarlavhalar.set("x-tema", tema ?? "tizim");

  const javob = NextResponse.next({
    request: { headers: sarlavhalar },
  });
  javob.headers.set("x-proxy", "1");
  return javob;
}

request: { headers } — sahifaga yetadigan so'rov sarlavhasi (sahifa uni headers() bilan o'qiydi, keyingi darsda). javob.headers — brauzerga ketadigan javob sarlavhasi. Kanonda bunday sarlavha yo'q; bu — keyingi bo'limdagi CSP tajribasining asosi.

6.2 Til aniqlash

Ko'p tilli saytda proxy /haqimda ni ichkarida /uz/haqimda ga rewrite qiladi yoki brauzer tiliga qarab /en ga yo'naltiradi. portfolio buni next-intl darsida aynan shu proxy'ga qo'shadi.

6.3 Optimistik auth — faqat birinchi qatlam

Proxy'ning eng mashhur ishi — "kirmagan foydalanuvchini /login ga yubor". Hujjat bu yerda ehtiyot bo'lishni so'raydi (tarjima): proxy optimistik tekshiruv uchun foydali, lekin u "to'liq sessiya boshqaruvi yoki avtorizatsiya yechimi sifatida ishlatilmasligi kerak".

Optimistik tekshiruv — tez, taxminiy tekshiruv: "cookie bormi?". Cookie bor bo'lsa ham u eskirgan yoki soxta bo'lishi mumkin. Haqiqiy tekshiruv (sessiya bazada bormi, foydalanuvchining huquqi yetadimi) — ma'lumotga yaqin joyda: DAL da va har Server Action ichida.

ts
export function proxy(request: NextRequest) {
  // optimistik: cookie umuman yo'q bo'lsa — darhol kirish sahifasiga
  if (!request.cookies.has("sessiya")) {
    return NextResponse.redirect(new URL("/kirish", request.url));
  }
  return NextResponse.next();
}

Haqiqiy auth'ni 28-qismda quramiz. Hozir qoida yetarli: proxy — eshikdagi tez filtr, seyf qulfi emas.

6.4 Tajriba: proxy va CSP nonce

CSP nonce (bir martalik raqam) — har so'rovda yangi tasodifiy satr: brauzer faqat shu raqamli <script> larni bajaradi. Next hujjati nonce'ni aynan proxy'da yaratishni ko'rsatadi. Kanon buni sinab ko'rdi (natijalar/tajriba/27-csp.txt):

Variant Natija
V1: 'unsafe-inline' bilan statik CSP 0 buzilish, sahifalar statik
V3: proxy'da nonce + 'strict-dynamic' 0 buzilish, lekin hamma sahifa ƒ

Nonce har so'rovda yangi — demak HTML ham har so'rovda yangi. Build'da tayyorlangan statik sahifalar yo'qoladi:

text
┌ ƒ /
├ ƒ /_not-found
├ ƒ /aloqa
├ ƒ /api/qayta-yangila
├ ƒ /blog                                  30d      1y

Kanon qarori — V1 (sahifalar statik qoladi). CSP'ning to'liq tahlili va boshqa variantlar (SRI, Trusted Types) — next.config chuqur darsida.

Tekshirib ko'ring: Proxy ichida har so'rovda GitHub API'ga fetch qilib, foydalanuvchi nomini sarlavhaga qo'shmoqchisiz. Nima bo'ladi?

Javob

Har mos sahifa GitHub javobini kutadi — «Haqiqiy natija» bo'limidagi bir necha millisekund yuzlab millisekundga aylanadi. GitHub ishlamasa, butun sayt to'xtaydi. Hujjat ham ogohlantiradi: proxy sekin ma'lumot olish uchun emas, fetch ning kesh sozlamalari (next.revalidate, next.tags) proxy'da ishlamaydi. Bunday ma'lumot — keshlangan Server Component'da ("use cache").

7. Hujumchi nigohi

Proxy — eshik. Hujumchi eshikni aylanib o'tish yo'lini qidiradi:

  • Matcher tirqishi. Proxy faqat matcher dagi yo'llarda ishlaydi. Hujjat ogohlantiradi: Server Action — alohida marshrut emas, u ishlatilgan sahifaga POST. Matcher o'zgarsa yoki action boshqa sahifaga ko'chsa, proxy himoyasi jimgina yo'qoladi. Shuning uchun har action ichida o'z tekshiruvi bo'lishi shart (Server Actions xavfsizligi).
  • Proxy'ni chetlab o'tish. 2025-yil 21-martda Next.js'da jiddiy zaiflik e'lon qilindi (CVE-2025-29927, xavf bahosi 9.1/10 — "kritik"). Next ichida ishlatiladigan x-middleware-subrequest sarlavhasini hujumchi o'zi qo'shib yuborsa, middleware butunlay o'tkazib yuborilardi. Auth'ni faqat middleware'da tekshirgan, o'zi xostlangan (next start, output: "standalone") saytlar ochiq qolgan; Vercel va Netlify'dagi saytlarga ta'sir qilmagan. Tuzatish — 15.2.3, 14.2.25, 13.5.9, 12.3.5 versiyalarida. Saboq doimiy: himoyaning yagona qatlami proxy bo'lmasin.
  • Bizning proxy'miz hech narsani yashirmaydi — u faqat 404 statusini to'g'rilaydi. Uni chetlab o'tgan odam baribir sahifaning o'z notFound() iga uriladi. Mana shunday bo'lishi kerak: proxy — tezlik va qulaylik, xavfsizlik esa ichkarida.

8. Portfolio qadami: noma'lum slug darhol 404

8.1 Fayl

src/proxy.ts — «portfolio ning proxy'si» bo'limidagi kod va tuzatilgan matcher. Boshqa fayl o'zgarmaydi: not-found.tsx va sahifalardagi notFound() joyida qoladi (ikkinchi himoya qatlami).

Diqqat: kanon tarixida birinchi matcher "/loyihalar/:slug" edi va .png xatosi holat 32 da topildi. Kanon keyin holat 27 faylini ham tuzatilgan matcher bilan almashtirdi. Siz to'g'ridan tuzatilgan variantni yozing.

8.2 Tekshiruv

bash
npm run lint
npm run tip
npm run build
npm run start

Boshqa terminalda:

bash
U=localhost:3000
curl -s -o /dev/null -w "%{http_code}\n" $U/loyihalar/yoq2
curl -s -o /dev/null -w "%{http_code}\n" $U/blog/rss.xml
curl -s -o /dev/null -w "%{http_code}\n" $U/loyihalar/vazifalar.png

Kutilgan: 404, 200, 200. Kanon o'lchovi: JS o'zgarmadi (proxy serverda ishlaydi), axe 0, hydration 0.

Hali commit qilmang: kanonda bu holat (27) keyingi dars bilan birga — after() qo'shilgach — bitta commit bo'ladi.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Proxy umuman ishlamaydi, xato ham yo'q Fayl app/ ichida yoki noto'g'ri papkada app/ bilan bir darajada: src/proxy.ts
CSS/JS/rasm yuklanmay qoldi matcher yo'q yoki juda keng Inkor shabloni yoki aniq yo'llar
Rasm 404 (/loyihalar/x.png) :slug nuqtali fayl nomini ham ushlaydi :slug([a-z0-9-]+)
Proxy does not support Edge runtime config.runtime: "edge" Proxy faqat Node'da (Next 16)
Har sahifa sekinlashdi Proxy ichida tarmoq so'rovi Proxy'da faqat tez tekshiruv
The "middleware" file convention is deprecated ogohlantirishi Eski nom — Next 16 da eskirgan (hali ishlaydi) Codemod: middleware-to-proxy
Matcher o'zgaruvchidan — ta'sir qilmaydi matcher build vaqtida statik o'qiladi Faqat literal satrlar

10. Mashqlar

Mashqlar kurs/mashqlar/23/26-proxy/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): qaysi javob?

  • /eski-blog → /blog, manzil satri ham o'zgarsin —
  • Noma'lum slug: 404 sahifa, manzil o'zgarmasin —
  • Slug mavjud, hech narsa qilish shart emas —
  • /api/... da kalit yo'q: darhol JSON xato —
Yechim
  • redirect — manzil o'zgarishi kerak. Lekin bunday o'zgarmas yo'naltirish uchun next.config dagi redirects soddaroq.
  • rewrite — ichkarida boshqa sahifa, tashqarida o'sha manzil.
  • next — NextResponse.next(): so'rov odatdagidek davom etadi.
  • Response — Response.json({...}, { status: 401 }): proxy javobni o'zi qaytaradi.

2-mashq (o'rta): matcher modelini sinang

«matcher modeli» blokidagi shablonniRegex ni oling va uchta shablonni sinang: /blog/:slug, /blog/:slug([a-z0-9-]+) va /loyihalar/:slug/rasm. Yo'llar: /blog/rss.xml, /blog/salom-dunyo, /loyihalar/bahor/rasm. Har juftlik uchun shablon | yo'l | natija chiqaring. Ichma-ich for...of — har shablon uchun har yo'l.

Yechim
js
function shablonniRegex(shablon) {
  const ichi = shablon.replace(
    /:(\w+)(\(([^)]*)\))?/g,
    (_, nom, __, re) => `(${re ?? "[^/]+"})`,
  );
  return new RegExp(`^${ichi}$`);
}

const shablonlar = [
  "/blog/:slug",
  "/blog/:slug([a-z0-9-]+)",
  "/loyihalar/:slug/rasm",
];
const yollar = [
  "/blog/rss.xml",
  "/blog/salom-dunyo",
  "/loyihalar/bahor/rasm",
];

for (const s of shablonlar) {
  const re = shablonniRegex(s);
  for (const y of yollar) {
    console.log(`${s} | ${y} | ${re.test(y)}`);
  }
}

Konsolda:

text
/blog/:slug | /blog/rss.xml | true
/blog/:slug | /blog/salom-dunyo | true
/blog/:slug | /loyihalar/bahor/rasm | false
/blog/:slug([a-z0-9-]+) | /blog/rss.xml | false
/blog/:slug([a-z0-9-]+) | /blog/salom-dunyo | true
/blog/:slug([a-z0-9-]+) | /loyihalar/bahor/rasm | false
/loyihalar/:slug/rasm | /blog/rss.xml | false
/loyihalar/:slug/rasm | /blog/salom-dunyo | false
/loyihalar/:slug/rasm | /loyihalar/bahor/rasm | true

Birinchi qator — aynan kanon xatosining blog varianti: /blog/:slug RSS faylini ham ushlaydi. Kanonning birinchi matcher'i buni maxsus istisno bilan ((?!rss\.xml$)) yopgan edi, lekin .png ni unutgan. Belgilar to'plamini cheklash — ikkalasini birdaniga yopadi.

3-mashq (qiyin): ikki tilli qaror

Keyinroq portfolio ga /en prefiksi qo'shiladi: /en/loyihalar/bahor. «proxy qarori» blokidagi qaror ni shunday o'zgartiring: yo'l /en bilan boshlansa, uni olib tashlab tekshirsin, va rewrite manzili tilni saqlasin (/en/_topilmadi, o'zbekchada — /uz/_topilmadi). Kanon next-intl darsida aynan shuni regex bilan qiladi; siz startsWith va slice bilan yozing.

Yechim
js
const LOYIHALAR = new Set(["vazifalar", "bahor", "landing"]);
const POSTLAR = new Set(["portfolio-v2", "server-components"]);

function qaror(yol) {
  const en = yol === "/en" || yol.startsWith("/en/");
  const til = en ? "en" : "uz";
  const ichki = en ? yol.slice(3) || "/" : yol;
  const [, bolim, slug] = ichki.split("/");
  const toplam = bolim === "loyihalar" ? LOYIHALAR : POSTLAR;
  return toplam.has(slug ?? "")
    ? "next()"
    : `rewrite /${til}/_topilmadi`;
}

for (const yol of [
  "/loyihalar/bahor",
  "/en/loyihalar/bahor",
  "/en/loyihalar/yoq",
  "/blog/yoq",
  "/en/blog/portfolio-v2",
]) {
  console.log(`${yol} → ${qaror(yol)}`);
}

Konsolda:

text
/loyihalar/bahor → next()
/en/loyihalar/bahor → next()
/en/loyihalar/yoq → rewrite /en/_topilmadi
/blog/yoq → rewrite /uz/_topilmadi
/en/blog/portfolio-v2 → next()

yol.slice(3) — /en ning uch belgisini olib tashlaydi. || "/" — faqat /en bo'lsa bo'sh satr qoladi, uni / ga aylantiramiz. Til rewrite manzilida saqlanadi — 404 sahifa ham to'g'ri tilda chiqishi uchun. Kanon buni bitta regex bilan qiladi: /^(?:\/(en|uz))?\/(loyihalar|blog)\/([a-z0-9-]+)(?:\/rasm)?$/.

4-mashq: Portfolio qadami

O'z portfolio ingizga src/proxy.ts ni qo'shing («Portfolio qadami» bo'limi). next build jadvalida ƒ Proxy (Middleware) qatori chiqishini, keyin next start ustida uchta curl natijasini tekshiring. Qo'shimcha: matcher ni ataylab eski variantga ("/loyihalar/:slug") qaytaring va /loyihalar/vazifalar.png ni so'rang — nima o'zgardi?

Yechim

Tuzatilgan matcher bilan: 404, 200, 200. Eski matcher bilan /loyihalar/vazifalar.png — 404: proxy fayl nomini slug deb o'yladi. Brauzerda loyiha sahifasini telefon o'lchamida ochsangiz, rasm o'rnida bo'sh joy qoladi (rasm optimizatori ichki so'rovi ham shu 404 ga uriladi — next/image darsida). Tekshirgach, tuzatilgan matcher'ni qaytaring. Commit'ni hali qilmang — keyingi dars bilan birga.

11. Real ishda

  • Auth'li ilovalarda proxy deyarli doim bor: "cookie yo'q → /login". Haqiqiy tekshiruv esa server kodida — bu qatlamlar (28-qism cookie va sessiyalar).
  • Ko'p tilli saytlarda proxy til prefiksini boshqaradi: next-intl, Paraglide kabi kutubxonalar o'z proxy funksiyasini beradi.
  • A/B testlar: proxy foydalanuvchini cookie bo'yicha ikki variantdan biriga rewrite qiladi — manzil bir xil, sahifa har xil.
  • Intervyu savollari: "Middleware va proxy farqi?" (nom va runtime), "rewrite va redirect farqi?", "Nega auth'ni faqat middleware'da qilish xavfli?" — CVE-2025-29927 ni bilish kuchli javob.
  • Eski loyihalarda middleware.ts va export const config = { runtime: "edge" } ni ko'p uchratasiz — Next 16 ga ko'chirishda codemod.

Xulosa

  • proxy.ts (Next 16 da middleware.ts o'rnida) — so'rov sahifaga yetmasdan ishlaydigan bitta funksiya, sukut bo'yicha Node'da.
  • Uch asosiy javob: next() — o'tkaz, rewrite() — manzilni o'zgartirmay boshqa sahifa, redirect() — brauzerni boshqa manzilga yubor.
  • matcher proxy'ni kerakli yo'llarga cheklaydi; :slug nuqtali fayl nomini ham ushlaydi — kanon .png xatosi, [a-z0-9-]+ bilan tuzatildi.
  • portfolio da proxy noma'lum slug'ni birinchi so'rovdayoq 404 qiladi; narxi bir necha millisekund.
  • Proxy — optimistik filtr: sekin so'rov ham, to'liq auth ham u yerda emas; CSP nonce esa butun saytni dinamik qiladi.

Keyingi dars: Request API'lar: cookies, headers, draftMode, after — sahifa va action ichida so'rovni o'qiymiz va javobdan keyin ishlaydigan after() bilan spam loglarini yozamiz.

Manbalar

  • Next.js hujjati (16.4.0, node_modules/next/dist/docs/): "Proxy" (getting started), proxy.js API reference — Matcher, Execution order, Runtime, Migration to Proxy, Unit testing; "Content Security Policy" — nextjs.org/docs/app/api-reference/file-conventions/proxy (2026-10-21 da o'qildi)
  • Kanon natijalari: natijalar/tajriba/06-notfound-status.txt, natijalar/27-proxy-status.txt, natijalar/tekshiruv-27.txt, natijalar/tajriba/27-csp.txt, natijalar/tajriba/39-edge.txt; matcher xatosi — natijalar/32-rasm-oldin.txt (holat 32 da topilgan)
  • Matcher jadvali (unstable_doesMiddlewareMatch) — biz ishga tushirdik, next 16.4.0, Node 24.21.0, 2026-10-21
  • CVE-2025-29927 — Next.js middleware avtorizatsiyani chetlab o'tish zaifligi (2025-03-21, CVSS 9.1): github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw; Vercel postmortem (2025-03-25): vercel.com/blog/postmortem-on-next-js-middleware-bypass (ikkalasi 2026-10-21 da o'qildi)
  • middleware.ts eskirgani — next 16.4.0 proxy.md "Version history" (v16.0.0, v15.5.0, v15.2.0) va build ogohlantirishi matni (paket kodi)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Next.js proxy.ts: so'rov sahifaga yetmasdan — matcher, rewrite va redirect — IlmHamroh