IlmHamroh
JavaScript Full-stack/23-qism. Next.js36/43-dars20 daqiqa
Mundarija (41)

next.config chuqur: xavfsizlik sarlavhalari, CSP, redirect va rewrite

Qisqacha: next.config.ts — Next.js ilovasining yagona sozlamalar fayli. Unda headers() har javobga sarlavha qo'shadi (CSP, HSTS, nosniff), redirects() eski manzilni yangisiga 308 yoki 307 bilan yo'naltiradi, rewrites() esa manzilni o'zgartirmasdan boshqa sahifani ko'rsatadi. Rasm optimallash (images), build chiqishi (output), yo'l prefiksi (basePath) ham shu yerda. Tiplangan havolalar (typedRoutes), React Compiler va Turbopack sozlamalari ham. portfolio da CSP — statik variant ('unsafe-inline'): nonce butun saytni dinamik qilardi, SRI esa Next 16.4 da hydration'ni buzdi — ikkalasi o'lchangan.

Bu darsda

  • next.config.ts dagi asosiy kalitlarni guruhlab tushuntira olasiz: so'rov, xavfsizlik, build, eksperimental.
  • headers() bilan CSP va xavfsizlik sarlavhalarini yozasiz va javobda ularni tekshirasiz.
  • Uch CSP varianti (statik, SRI, nonce) qanday natija berganini o'lchov bilan bilasiz.
  • redirects() bilan v1 (GitHub Pages) manzillarini v2 ga yo'naltirasiz, 308 va 307 farqini bilasiz.
  • Next so'rovni qaysi tartibda tekshirishini (headers → redirects → proxy → rewrites → sahifalar) chizib bera olasiz.

Oldin bilishingiz kerak: Vite II: vite.config, Loyiha yaratish va tuzilishi, proxy.ts, next/image, Xatolar va instrumentation.

1. Nega bu kerak?

Loyiha yaratish darsida next.config.ts ni birinchi ko'rgansiz: uch-to'rt qator, "hozircha tegmaymiz". O'shandan beri portfolio ancha o'sdi. Endi bir nechta savol ochiq qoldi.

Birinchisi — xavfsizlik. Loyiha yaratish darsida javobda X-Powered-By: Next.js sarlavhasini ko'rgandingiz: server hujumchiga o'zini tanishtiradi. CSP esa umuman yo'q. 13-qismda vazifalar uchun CSP yozgandingiz (Brauzer xavfsizlik modeli) — Next'da u qayerga yoziladi?

Ikkinchisi — eski manzillar. v1 GitHub Pages'da turardi va manzillar …/loyihalar/bahor/index.html ko'rinishida edi. Do'stlaringiz o'sha havolalarni ulashgan, Google ularni eslab qolgan. v2 da bunday fayl yo'q — 404.

O'xshatish: bino. Sahifalar — xonalar. next.config.ts — binoning qo'riqxonasi: kirish eshigidagi qoidalar (sarlavhalar), "bu xona ko'chdi, 3-qavatga o'ting" yozuvlari (redirect), va binoning o'zi qanday qurilishi (build sozlamalari).

2. Faylning xaritasi

2.1 Kalitlar guruhlari

next.config.ts da yuzga yaqin kalit bor (16.4 hujjatida 80 dan ortiq sahifa). Ularni yodlash shart emas. To'rt guruhga ajratsangiz, qayerdan qidirishni bilasiz:

Guruh Kalitlar portfolio da
So'rov va javob headers, redirects, rewrites, poweredByHeader, trailingSlash birinchi uchtasidan ikkitasi
Rasm va resurslar images, assetPrefix, basePath yo'q (sukut yetadi)
Build va render output, cacheComponents, partialPrefetching, reactCompiler, typedRoutes, turbopack ikkitasi
Eksperimental experimental.* serverActions.bodySizeLimit

2.2 Skelet fayli to'liq

Loyiha yaratish darsida create-next-app yaratgan faylni ko'rgandingiz va "to'liq — keyinroq" degandik. Mana u:

ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
  cacheComponents: true,
  partialPrefetching: true,
  turbopack: {
    rules: {
      "*.css": {
        loaders: ["@tailwindcss/turbopack"],
        as: "*.css",
      },
    },
  },
};

export default nextConfig;

turbopack.rules — Turbopack'ga "bu turdagi fayllarni avval shu loader (yuklagich) orqali o'tkaz" deydi. Loader — faylni bundler tushunadigan ko'rinishga aylantiruvchi kichik dastur (Vite plaginlari bilan o'xshash g'oya). Bu yerda har .css fayl Tailwind orqali o'tadi, natija yana .css (as). Kanon Stil va tema darsida Tailwind'ni olib tashladi — bu blok ham ketdi.

Next 15 da bu sozlama experimental.turbopack ichida edi; 16 da yuqori darajaga chiqdi (rasmiy yangilash hujjati). Eski maqolalarda experimental ichida ko'rsangiz — sababi shu.

2.3 portfolio dagi fayl (holat 37)

ts
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";

const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");

const dev = process.env.NODE_ENV === "development";
const CSP = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline'${dev ? " 'unsafe-eval'" : ""}`,
  "style-src 'self' 'unsafe-inline'",
  "img-src 'self' blob: data:",
  "font-src 'self'",
  "connect-src 'self'",
  "object-src 'none'",
  "base-uri 'self'",
  "form-action 'self'",
  "frame-ancestors 'none'",
  ...(dev ? [] : ["upgrade-insecure-requests"]),
].join("; ");

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
  poweredByHeader: false,
  experimental: {
    serverActions: {
      bodySizeLimit: "64kb",
    },
  },
  async headers() {
    return [
      {
        source: "/:path*",
        headers: [
          { key: "Content-Security-Policy", value: CSP },
          { key: "X-Content-Type-Options", value: "nosniff" },
          {
            key: "Referrer-Policy",
            value: "strict-origin-when-cross-origin",
          },
          {
            key: "Permissions-Policy",
            value:
              "camera=(), microphone=(), geolocation=(), browsing-topics=()",
          },
          {
            key: "Strict-Transport-Security",
            value: "max-age=63072000; includeSubDomains",
          },
        ],
      },
    ];
  },
  async redirects() {
    return [
      { source: "/index.html", destination: "/", permanent: true },
      { source: "/:yol*/index.html", destination: "/:yol*", permanent: true },
      { source: "/404.html", destination: "/", permanent: false },
    ];
  },
};

export default withNextIntl(nextConfig);

Kanon kodidagi izohlar olib tashlandi (ular pastda matnda), Referrer-Policy qatori telefonga sig'ishi uchun ikkiga bo'lindi. Ikki qator 70 belgidan uzun — ular kanondan aynan. Fayl bo'lib-bo'lib o'qiladi.

withNextIntl(nextConfig) — plagin o'rami. Kutubxona sozlamalarni o'z funksiyasidan o'tkazib, kerakli qismini qo'shadi. next-intl bu yerda src/i18n/request.ts ni ulaydi (next-intl). Sentry (withSentryConfig) va bundle analyzer ham shunday ulanadi.

3. Xavfsizlik sarlavhalari

3.1 headers() qanday ishlaydi

headers() — massiv qaytaradigan async funksiya. Har element: source (qaysi yo'llarga) va headers (qaysi sarlavhalar). "/:path*" — "har qanday yo'l": :path — nomli parametr, * — "nol yoki ko'p segment". Demak sarlavhalar har sahifa, har rasm va har API javobiga qo'shiladi.

3.2 Beshta sarlavha

Sarlavha Nima qiladi
Content-Security-Policy Brauzerga "faqat shu manbalardan skript, stil, rasm yukla" deydi — XSS'ga qarshi asosiy to'siq
X-Content-Type-Options: nosniff Brauzer fayl turini "taxmin" qilmasin: rasm deb kelgan fayl skript sifatida bajarilmaydi
Referrer-Policy Boshqa saytga o'tganda manzilning faqat domeni ketadi, yo'l va query emas
Permissions-Policy Kamera, mikrofon, joylashuv — sahifada umuman o'chiq
Strict-Transport-Security HSTS: brauzer 2 yil davomida bu saytga faqat HTTPS orqali kiradi

max-age=63072000 — soniyalarda: 63 072 000 ÷ 86 400 = 730 kun, ya'ni 2 yil. HSTS ehtiyotkorlik talab qiladi: brauzer uni eslab qoladi. Sayt HTTPS'siz qolsa, foydalanuvchi ikki yil kira olmaydi. Shuning uchun u faqat HTTPS'ga ishonch hosil bo'lgach qo'yiladi. Lokal next start (http) da brauzer HSTS'ni e'tiborsiz qoldiradi — u faqat HTTPS javobida kuchga kiradi.

poweredByHeader: false — X-Powered-By: Next.js sarlavhasini o'chiradi. Bu xavfsizlikni kuchaytirmaydi, faqat hujumchiga "qaysi texnologiya, qaysi zaiflikni qidirish" haqida tekin maslahatni olib tashlaydi.

3.3 Biz tekshirdik: javob sarlavhalari

Kanon holat 37 da next start qildi va bosh sahifa javobini o'qidi:

text
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), browsing-topics=()
Strict-Transport-Security: max-age=63072000; includeSubDomains

X-Powered-By yo'q. CSP'da 'unsafe-eval' yo'q — u faqat dev'da qo'shiladi: React dev rejimida xato stack'larini tiklash uchun eval ishlatadi (Next CSP hujjati). Production'da esa upgrade-insecure-requests bor: sahifadagi http:// havolalarni brauzer o'zi https:// ga ko'taradi.

Eng muhim tekshiruv — CSP saytni buzmadimi? Kanon ssenariysi 4 sahifani ochib, «Qorong'i» tugmasini va teg filtrini bosdi:

text
hydration ishladi (Qorong'i bosilgach data-tema): qorongi | filtr: 1 ta loyiha ko'rsatilmoqda
CSP buzilishlari: 0

3.4 Jonli: CSP satrini yig'ish

CSP — bitta uzun satr. Kanon uni massivdan yig'adi: har direktiva alohida qator, o'qish va o'zgartirish oson. Dev va prod farqini o'zingiz ko'ring — dev ni true ga o'zgartirib qayta ishga tushiring:

js
function buildCsp(dev) {
  return [
    "default-src 'self'",
    `script-src 'self' 'unsafe-inline'${dev ? " 'unsafe-eval'" : ""}`,
    "object-src 'none'",
    "frame-ancestors 'none'",
    ...(dev ? [] : ["upgrade-insecure-requests"]),
  ].join("; ");
}

const prod = buildCsp(false);
console.log(prod.split("; ").length); // 5
console.log(prod.includes("unsafe-eval")); // false
console.log(buildCsp(true).split("; ")[1]);

Konsolda:

text
5
false
script-src 'self' 'unsafe-inline' 'unsafe-eval'

...(dev ? [] : [...]) — spread bilan shartli element: dev'da bo'sh massiv yoyiladi (hech narsa qo'shilmaydi), prod'da bitta direktiva. Bu yerda 5 ta direktiva — qisqartirilgan namuna; kanonda 11 ta.

Tekshirib ko'ring: CSP'da connect-src 'self'. Kelajakda Sentry ulasangiz (oldingi dars), brauzer unga xato yubora oladimi?

Javob

Yo'q. connect-src — fetch, XMLHttpRequest, WebSocket qayerga ulanishi mumkinligini belgilaydi. 'self' — faqat o'z saytingiz. Sentry manzilini (https://….ingest.sentry.io) connect-src ga qo'shmasangiz, brauzer so'rovni bloklaydi va konsolda CSP buzilishi chiqadi. Har tashqi xizmat — CSP'ga bitta qo'shimcha.

4. CSP: uch yo'l va o'lchov

4.1 Muammo: Next'ning inline skriptlari

Ideal CSP — script-src 'self': faqat o'z fayllaringiz, sahifa ichidagi <script>…</script> emas. XSS hujumi aynan ichki skriptni joylashga urinadi (XSS).

Lekin Next.js sahifa HTML'ining o'ziga skriptlar qo'yadi: self.__next_f.push(…) qatorlari — server yuborgan RSC payload bo'laklari. Ular inline. Qat'iy CSP ularni bloklaydi.

4.2 Biz o'lchadik: uch variant

Kanon proxy.ts bosqichida uch yo'lni sinadi (4 sahifa, keyin tema tugmasi va filtr — hydration tekshiruvi):

Variant Sahifalar CSP buzilishi Hydration
V1: headers(), script-src 'self' 'unsafe-inline' statik qoldi 0 ishlaydi
V2: experimental.sri + script-src 'self' statik qoldi 66 ishlamaydi
V3: proxy.ts da nonce + 'strict-dynamic' hammasi dinamik 0 ishlaydi

V2 dagi buzilish xabari (kanon xom chiqishidan, bittasi):

text
Executing inline script violates the following Content Security Policy directive 'script-src 'self''. Either the 'unsafe-inline' keyword, a hash ('sha256-qVIaYjemSeyZZlID1IWHfcAiNXtDya/toAxIJOAVbkA='), or a nonce ('nonce-...') is required to enable inline execution. The action has been blocked.

Tarjimasi: "Ichki skriptni bajarish script-src 'self' direktivasini buzadi. Ichki bajarish uchun 'unsafe-inline', xesh yoki nonce kerak. Amal bloklandi".

4.3 Har variantning ma'nosi

SRI (Subresource Integrity — tashqi faylning butunligi) — <script src> ga faylning xeshini qo'shadi. Next 16.4 hujjati SRI'ni (eksperimental) nonce'ga muqobil sifatida taklif qiladi: "statik generatsiyani saqlagan holda qat'iy CSP". Kanon o'lchovida Next 16.4 bilan bu ishlamadi: SRI faqat tashqi fayllarni himoya qiladi, self.__next_f.push kabi ichki skriptlar baribir bloklandi — 66 buzilish, tugmalar o'lik. Hujjat bilan o'lchov farq qildi — biz o'lchovga ishonamiz.

Nonce (bir martalik raqam) — har so'rovda yangi tasodifiy satr: server uni CSP sarlavhasiga va har <script> ga qo'yadi. Hujumchi raqamni oldindan bilmaydi. Bu eng kuchli himoya. Narxi: nonce har so'rovda boshqa, demak sahifani build vaqtida tayyorlab bo'lmaydi. Build jadvalida hamma ○ va ◐ sahifalar ƒ ga aylandi — CDN keshi, TTFB 4–12 ms yo'qoladi. Bundan tashqari build avval /_not-found: uncached or runtime data xatosi bilan to'xtadi, root layout'ga qo'shimcha sozlama kerak bo'ldi.

Trusted Types (13-qismdan tanish — Brauzer xavfsizlik modeli) nonce bilan birga sinaldi: to'liq yuklashda ishladi, lekin blog postiga client navigatsiyada Turbopack chunk yuklagichi bloklandi, Next to'liq qayta yuklashga o'tdi (Falling back to browser navigation).

4.4 Kanon qarori: V1

portfolio — statik sayt: foydalanuvchi kiritgan HTML yo'q, dangerouslySetInnerHTML faqat o'z Markdown'imizdan. Shuning uchun V1 tanlandi: 'unsafe-inline' faqat script-src da, qolgan 10 direktiva qat'iy. Bu murosa, va u ochiq yozilgan.

Qachon nonce'ga o'tish kerak? Foydalanuvchi kontenti bor sayt (izohlar, profil), to'lov sahifasi, admin panel — u yerda dinamik render baribir bor, XSS xavfi esa yuqori. Nonce'ni yozish tartibi — proxy.ts darsida.

Tekshirib ko'ring: Nega nonce'ni next.config.ts dagi headers() da yozib bo'lmaydi?

Javob

headers() — build vaqtida bir marta o'qiladigan ro'yxat: har javobga bir xil sarlavha. Nonce esa har so'rovda yangi bo'lishi kerak. Har so'rovda kod ishlaydigan joy — proxy.ts. Shuning uchun nonce varianti proxy'da yoziladi.

5. redirects(): v1 manzillari

5.1 Muammo va yechim

v1 da har sahifa — papka ichidagi index.html: /loyihalar/bahor/index.html. GitHub Pages /loyihalar/bahor/ ni ham ochardi, lekin to'liq shakl ham internetda tarqalgan. Kanon uchta qoida yozdi:

source destination permanent
/index.html / true (308)
/:yol*/index.html /:yol* true (308)
/404.html / false (307)

5.2 308 va 307

permanent: true — 308 (doimiy). Brauzer va Google eslab qoladi: keyingi safar to'g'ridan yangi manzilga boradi, Google eski manzilning "obro'sini" yangisiga o'tkazadi. permanent: false — 307 (vaqtinchalik): hech kim eslab qolmaydi.

Nega 301/302 emas? Hujjat sababini aytadi: ko'p brauzerlar 301/302 da so'rov metodini GET ga o'zgartirib yuboradi. 307/308 metodni saqlaydi — POST forma yo'naltirilsa, POST bo'lib qoladi. 404.html uchun 307: bu "ko'chgan sahifa" emas, shunchaki eski xato sahifasi.

5.3 Biz tekshirdik

Kanon tajribasi (holat 37, next start):

text
/loyihalar/bahor/index.html -> 308 http://localhost:3131/loyihalar/bahor
/index.html -> 308 http://localhost:3131/
/haqimda/index.html -> 308 http://localhost:3131/haqimda
/404.html -> 307 http://localhost:3131/

Har qatorda: so'ralgan yo'l → status → yangi manzil. :yol* qiymati (loyihalar/bahor) manzilga ko'chirildi. Hujjatga ko'ra query qismi ham o'tadi: /haqimda/index.html?x=1 → /haqimda?x=1.

5.4 Jonli: naqsh qanday solishtiriladi

Next naqshlarni path-to-regexp kutubxonasi bilan solishtiradi. Uning asosiy g'oyasini kichik model bilan ko'ramiz: :nom — bitta segment, :nom* — nol yoki ko'p segment. Bu o'quv modeli — haqiqiy kutubxona ko'proq narsani biladi (:nom+, :nom?, regex):

js
function toRegExp(source) {
  const body = source
    .replace(/\./g, "\\.")
    .replace(/\/:(\w+)\*/g, "(?:/(?<$1>.*))?")
    .replace(/\/:(\w+)/g, "/(?<$1>[^/]+)");
  return new RegExp(`^${body}$`);
}

const rule = toRegExp("/:yol*/index.html");
const tests = ["/loyihalar/bahor/index.html", "/index.html", "/blog"];
for (const path of tests) {
  const m = rule.exec(path);
  console.log(path, "->", m ? `/${m.groups.yol ?? ""}` : "mos emas");
}

Konsolda:

text
/loyihalar/bahor/index.html -> /loyihalar/bahor
/index.html -> /
/blog -> mos emas

(?<yol>…) — nomli guruh, m.groups.yol bilan o'qiladi. /index.html da :yol* bo'sh — shuning uchun natija /. Kanon baribir /index.html uchun alohida qoida yozgan: aniq qoida o'qishga oson va naqshning chekka holatiga tayanmaydi.

6. rewrites() va so'rov tartibi

6.1 Redirect va rewrite farqi

Rewrite (qayta yozish) — manzil o'zgarmaydi, lekin boshqa sahifa ko'rsatiladi. Foydalanuvchi /blog ni ko'radi, server esa ichida /maqolalar ni chizadi. Brauzer hech narsa bilmaydi.

Redirect Rewrite
Brauzer manzili o'zgaradi o'zgarmaydi
Javob 307/308 + Location to'g'ridan sahifa (200)
So'rovlar soni 2 1
Odatiy ish ko'chgan sahifa tashqi xizmatni o'z domeningizda ko'rsatish, A/B test

portfolio da rewrites() yo'q. Lekin rewrite bor — proxy.ts da: noma'lum slug NextResponse.rewrite bilan 404 sahifaga yuboriladi, manzil esa o'zgarmaydi. Statik qoidaga sig'maydigan, har so'rovda tekshiruv kerak bo'lgan rewrite — proxy'da; oddiy, o'zgarmas qoida — next.config.ts da.

6.2 Next so'rovni qaysi tartibda tekshiradi

Rewrites hujjatidagi ro'yxat bo'yicha. Kim birinchi ishlaydi — muhim: masalan redirect proxy'dan oldin, demak /index.html proxy'ga yetib bormaydi.

flowchart TB
  A["so'rov"] --> H["1. headers()"]
  H --> R["2. redirects()"]
  R --> P["3. proxy.ts"]
  P --> B["4. rewrites: beforeFiles"]
  B --> F["5. public/, _next/static,<br/>statik sahifalar"]
  F --> AF["6. rewrites: afterFiles"]
  AF --> D["7. dinamik marshrutlar<br/>[slug]"]
  D --> FB["8. rewrites: fallback"]
  FB --> N["404"]

rewrites() massiv qaytarsa — u afterFiles (6-qadam): mavjud sahifalar ustun. { beforeFiles, afterFiles, fallback } obyekt qaytarsa — har guruh o'z joyida.

7. Qolgan kalitlar

7.1 images

next/image sozlamalari (next/image). portfolio rasmlari public/ da, shuning uchun sukut yetadi. Tashqi rasm kerak bo'lsa — remotePatterns:

ts
images: {
  remotePatterns: [
    { protocol: "https", hostname: "avatars.githubusercontent.com" },
  ],
},

Next 16 da bir nechta sukut o'zgardi (rasmiy yangilash hujjati). minimumCacheTTL 60 soniyadan 4 soatga oshdi. qualities — endi faqat [75]: boshqa sifat kerak bo'lsa, ro'yxatga qo'shiladi. imageSizes dan 16 olib tashlandi, lokal IP manzillardagi rasmlar sukut bo'yicha bloklanadi. Eski images.domains — eskirgan, o'rniga remotePatterns.

7.2 output

Qiymat Natija Qachon
(yo'q) .next/ — next start bilan ishlaydi Vercel, oddiy server
"standalone" .next/standalone/ — o'zi ishlaydigan papka + server.js Docker, VPS
"export" out/ — faqat HTML/CSS/JS fayllar GitHub Pages kabi statik hosting

Kanon standalone ni sinadi: papka 32 MB, node_modules esa 471 MB (Deploy I: Vercel darsida). export portfolio uchun mos emas: Server Action (aloqa formasi), proxy.ts va Route Handler server talab qiladi.

7.3 basePath

Sayt domen ildizida emas, ichki yo'lda tursa: basePath: "/portfolio" — hamma sahifa /portfolio/... bo'ladi, <Link> prefiksni o'zi qo'shadi. Qiymat build vaqtida JS'ga yoziladi — o'zgarsa, qayta build kerak. portfolio o'z domenida, shuning uchun yo'q.

7.4 typedRoutes — biz sinab ko'rdik

typedRoutes: true — <Link href> ni TypeScript bilan tekshiradi: mavjud bo'lmagan yo'l yozilsa, tur xatosi. G'oya ajoyib. Biz uni portfolio (holat 43 nusxasi) da yoqdik va ataylab xato yo'l (/loyihalr) yozdik. npm run tip natijasi:

text
src/i18n/navigation.tsx(26,20): error TS2322: Type 'string' is not assignable to type 'UrlObject | RouteImpl<string>'.

Kutilgan xato chiqmadi, kutilmagani chiqdi. Ikki sabab:

  • /loyihalr haqiqatan "to'g'ri" yo'l: ildizda [locale] dinamik segmenti bor, u istalgan bitta segmentni qabul qiladi. Ichida yana [...yoq] — hammasini ushlovchi segment (catch-all), 404 sahifasi uchun. Natijada har qanday yo'l tur jihatidan to'g'ri.
  • Bizning o'z Link imiz (i18n/navigation.tsx) href ni oddiy string qilib oladi. typedRoutes next/link ga faqat tekshirilgan yo'l turini beradi — oddiy satr endi o'tmaydi.

Saboq: typedRoutes qat'iy, statik yo'llar ko'p bo'lgan loyihada foydali. Butun sayt [locale] ostida bo'lsa, u deyarli hech narsani tekshirmaydi, ammo o'ramlarni qayta yozishni talab qiladi. Kanonga kirmadi.

7.5 reactCompiler

React Compiler — komponentlarni avtomatik memo qiladigan vosita. Next'da reactCompiler: true va babel-plugin-react-compiler paketi kerak. Next uni hamma faylga emas, faqat JSX yoki hook bor fayllarga qo'llaydi — build tezligi uchun. Kanonga kirmadi: portfolio da client komponentlar 10 ga yaqin va kichik, qayta render muammosi o'lchanmagan. Batafsil — Next.js performance darsida.

7.6 experimental

experimental.* — barqaror emas, keyingi versiyada nomi yoki xulqi o'zgarishi mumkin. Kanonda bittasi: serverActions.bodySizeLimit: "64kb" — Server Action so'rovi hajmi chegarasi (sukut 1 MB; aloqa xabari 2 000 belgi, shuning uchun 64 KB yetadi — Server Actions xavfsizligi). Tajribada yana bittasi sinaldi va qaytarildi: experimental.sri.

Qoida: experimental dagi har kalit yonida izoh — nega kerak va qaysi versiyada sinalgan.

8. Portfolio qadami: next.config.ts (holat 37)

Holat 37 ning ikkinchi qismi. Oldingi darsda instrumentation.ts qo'shildi; bu darsda next.config.ts ga:

O'zgarish Nima beradi
poweredByHeader: false X-Powered-By yo'q
headers() CSP (V1), nosniff, Referrer-Policy, Permissions-Policy, HSTS
redirects() v1 manzillari: /:yol*/index.html → 308, /404.html → 307

Hozir commit qilmang — holat 37 ning commit'i Env o'zgaruvchilar darsida.

Kanon tekshiruvi (holat 37): lint, tip, format — 0; build 9 s; sarlavhalar javobda; CSP bilan hydration ishlaydi, buzilish 0; modal, forma va i18n regressiya ssenariylari o'zgarmadi; axe 0. JS hajmi o'zgarmadi — next.config.ts serverda o'qiladi.

Ma'lum ogohlantirish (holat 34 dan beri): build metadataBase property … is not set ni uch marta yozadi. Manbasi — Next o'zi yaratadigan root /_not-found sahifasi (bizning layout'siz). Ta'siri yo'q, kanon uni ochiq qoldirgan.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Sarlavha javobda yo'q source mos emas ("/" faqat bosh sahifa) "/:path*"
CSP qo'shilgach tugmalar ishlamaydi script-src 'self' Next'ning inline skriptlarini bloklaydi V1 ('unsafe-inline') yoki nonce
Dev'da CSP xatolari, prod'da yo'q Dev eval ishlatadi Dev'da 'unsafe-eval'
Redirect cheksiz aylanadi destination yana source ga mos Naqshni toraytiring
config o'zgardi, natija eskicha next.config.ts faqat ishga tushganda o'qiladi next dev ni qayta ishga tushiring; prod — qayta build
images.domains is deprecated Eski kalit remotePatterns
Eski maqoladagi experimental.turbopack Next 15 sintaksisi Yuqori darajadagi turbopack

10. Mashqlar

Mashqlar kurs/mashqlar/23/36-next-config/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): qaysi kalit?

  • X-Powered-By ni o'chirish —
  • Eski manzil doimiy ko'chdi — redirect permanent: true, status
  • Manzil o'zgarmay boshqa sahifa ko'rsatilsin —
  • Docker uchun o'zi ishlaydigan papka — output: ""
  • Tashqi domendagi rasm uchun ruxsat — images.
Yechim
  • poweredByHeader — false qilinadi.
  • 308 — doimiy; vaqtinchalik — 307. Ikkalasi ham metodni saqlaydi.
  • rewrites — brauzer manzili o'zgarmaydi; redirect'da o'zgaradi.
  • standalone — .next/standalone + server.js.
  • remotePatterns — images.domains eskirgan.

2-mashq (o'rta): :nom va :nom* modeli

«Naqsh qanday solishtiriladi» blokidagi toRegExp ni oling. Endi qoida yo'naltirishni ham hisoblasin: applyRedirect(rule, path) funksiyasi { source, destination, permanent } qoidani oladi va mos kelsa "308 /yangi/yol" ko'rinishidagi satr, mos kelmasa null qaytarsin. destination dagi :nom va :nom* o'rniga topilgan qiymat qo'yilsin. Sinang: /old-blog/:slug → /blog/:slug (vaqtinchalik) uchun /old-blog/salom va /old-blog/a/b.

Yechim
js
function toRegExp(source) {
  const body = source
    .replace(/\./g, "\\.")
    .replace(/\/:(\w+)\*/g, "(?:/(?<$1>.*))?")
    .replace(/\/:(\w+)/g, "/(?<$1>[^/]+)");
  return new RegExp(`^${body}$`);
}

function applyRedirect(rule, path) {
  const m = toRegExp(rule.source).exec(path);
  if (!m) return null;
  const target = rule.destination.replace(
    /:(\w+)\*?/g,
    (_, name) => m.groups[name] ?? "",
  );
  const status = rule.permanent ? 308 : 307;
  return `${status} ${target.replace(/\/$/, "") || "/"}`;
}

const rule = {
  source: "/old-blog/:slug",
  destination: "/blog/:slug",
  permanent: false,
};
console.log(applyRedirect(rule, "/old-blog/salom"));
console.log(applyRedirect(rule, "/old-blog/a/b"));

Konsolda:

text
307 /blog/salom
null

:slug bitta segment ([^/]+), shuning uchun /old-blog/a/b mos kelmadi — Next hujjatidagi "nested paths emas" qoidasi aynan shu. Ichma-ich yo'llar uchun :slug* yoziladi. Oxirgi replace — bo'sh :yol* dan qolgan oxirgi / ni olib tashlaydi.

3-mashq (qiyin): CSP auditori

auditCsp(csp) funksiyasini yozing. U CSP satrini direktivalarga ajratadi (; bo'yicha, birinchi so'z — nom) va ogohlantirishlar ro'yxatini qaytaradi: (1) object-src, base-uri, frame-ancestors dan birortasi yo'q bo'lsa — "<nom> yo'q"; (2) script-src da 'unsafe-eval' bo'lsa — "script-src: unsafe-eval"; (3) 'unsafe-inline' bo'lsa — "script-src: unsafe-inline (murosa)". Kanon CSP'sini va "default-src 'self'; script-src 'self' 'unsafe-eval'" ni tekshiring.

Yechim
js
const REQUIRED = ["object-src", "base-uri", "frame-ancestors"];

function auditCsp(csp) {
  const directives = new Map(
    csp.split("; ").map((part) => {
      const [name, ...values] = part.trim().split(" ");
      return [name, values];
    }),
  );
  const warnings = REQUIRED.filter((n) => !directives.has(n)).map(
    (n) => `${n} yo'q`,
  );
  const script = directives.get("script-src") ?? [];
  if (script.includes("'unsafe-eval'")) {
    warnings.push("script-src: unsafe-eval");
  }
  if (script.includes("'unsafe-inline'")) {
    warnings.push("script-src: unsafe-inline (murosa)");
  }
  return warnings;
}

const canon =
  "default-src 'self'; script-src 'self' 'unsafe-inline'; " +
  "object-src 'none'; base-uri 'self'; frame-ancestors 'none'";
console.log(auditCsp(canon));
const weak = "default-src 'self'; script-src 'self' 'unsafe-eval'";
console.log(auditCsp(weak));

Konsolda:

text
[ 'script-src: unsafe-inline (murosa)' ]
[
  "object-src yo'q",
  "base-uri yo'q",
  "frame-ancestors yo'q",
  'script-src: unsafe-eval'
]

Kanon CSP'sida faqat bitta, oldindan qabul qilingan murosa qoldi. Ikkinchi CSP'da uchta muhim direktiva yo'q va eval ochiq. Haqiqiy vosita — Google'ning CSP Evaluator — xuddi shunday ishlaydi, faqat qoidalari ko'proq. Brauzer konsoli massivni boshqacha ko'rsatadi ((1) ['…']) — ma'nosi bir xil.

4-mashq: Portfolio qadami

O'z portfolio ingizning next.config.ts iga poweredByHeader: false, headers() (CSP V1 va to'rt sarlavha) va v1 manzillari uchun redirects() ni qo'shing. npm run build && npm run start, keyin brauzerda DevTools → Network → bosh sahifa → Response Headers. Sarlavhalar bormi? Konsolda CSP xatosi yo'qmi? Tema tugmasi ishlayaptimi? /loyihalar/vazifalar/index.html ni oching — manzil o'zgarishi kerak. Commit — keyingi darsda.

Yechim

Namuna — «portfolio dagi fayl» bo'limi. Terminalda tekshirish: curl -I http://localhost:3000/ (javob sarlavhalari) va curl -I http://localhost:3000/loyihalar/vazifalar/index.html — 308 va location: /loyihalar/vazifalar. CSP xatosi chiqsa — script-src dan 'unsafe-inline' tushib qolgan. HSTS lokal http'da kuchga kirmaydi — bu normal.

11. Real ishda

  • Xavfsizlik auditi — kompaniyalar saytni securityheaders.com yoki Mozilla Observatory bilan tekshiradi; CSP, HSTS, nosniff yo'qligi — birinchi topilmalar. Bu sarlavhalar odatda next.config.ts da yoki proxy/CDN darajasida qo'yiladi.
  • Sayt ko'chishi — domen yoki tuzilma o'zgarganda redirect ro'yxati yuzlab qator bo'ladi; SEO jamoasi har eski manzil 308 bilan to'g'ri joyga borishini talab qiladi. Katta ro'yxatlarni Next hujjati proxy va kesh bilan qilishni tavsiya qiladi.
  • Intervyu savollari: "301 va 308 farqi?", "Redirect va rewrite farqi?", "CSP'da 'unsafe-inline' nega xavfli va Next'da nonce narxi nima?".
  • Plagin o'ramlari — withNextIntl, withSentryConfig, withBundleAnalyzer bir-biriga o'raladi: withA(withB(config)). Real loyihadagi next.config.ts ko'pincha shunday ko'rinadi.

Xulosa

  • next.config.ts — to'rt guruh: so'rov/javob, rasm, build/render, eksperimental; hammasini yodlash shart emas.
  • headers() — har javobga CSP, nosniff, Referrer-Policy, Permissions-Policy, HSTS; poweredByHeader: false.
  • CSP o'lchovi: statik V1 ishlaydi; SRI Next 16.4 da hydration'ni buzdi (66 buzilish); nonce hammasini dinamik qiladi.
  • redirects() — 308 doimiy, 307 vaqtinchalik; v1 …/index.html manzillari yangi yo'lga ko'chdi.
  • So'rov tartibi: headers → redirects → proxy → rewrites → fayllar → dinamik marshrutlar.
  • typedRoutes [locale] va hammasini ushlovchi segment bor loyihada deyarli hech narsani tekshirmaydi — o'lchab ko'rildi.

Keyingi dars: Env o'zgaruvchilar va sirlar Next.js'da — NEXT_PUBLIC_ va server env farqi, Zod bilan tekshiruv va "bitta Docker image — ko'p muhit" tuzog'i.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
next.config chuqur: xavfsizlik sarlavhalari, CSP, redirect va rewrite — IlmHamroh