Next.js robots.txt bị chặn: tôi bắt được lỗi proxy ngay trên sản phẩm thật của mình (Kiwi)
robots.txt, llms.txt, sitemap.xml và manifest.json trên Kiwi — sản phẩm thật của tôi — đều trả về đúng một trang đăng nhập thay vì file thật, trong khi /.well-known/ vẫn 404 bình thường. Tôi đối chiếu hiện tượng này với tài liệu chính thức của Next.js về execution order của Proxy (middleware cũ) và với spec robots.txt của Google, để chỉ ra nguyên nhân nhiều khả năng nằm ở matcher chặn request trước khi Next.js kịp phục vụ các file quy ước.

Kiwi đã ngừng phát triển từ tháng 6/2026; các ví dụ trong bài được giữ lại để tham khảo.
Trong bài này
Next.js robots.txt bị chặn không phải vì tôi viết sai file — mà vì tầng Proxy (middleware cũ) đứng chắn trước khi Next.js kịp phục vụ nó. Tôi phát hiện ra chuyện này sáng nay khi tự tay gọi từng URL hệ thống trên kiwi.beru.io.vn, sản phẩm thật của tôi, và nhận về đúng một trang đăng nhập cho mọi request lạ. Bài này là nhật ký kiểm chứng: request thật, output thật, đối chiếu với tài liệu chính thức của Next.js và Google, không đoán mò.
Next.js robots.txt bị chặn: chuyện bắt đầu từ đâu
Tôi vận hành hai domain: beru.io.vn (blog kỹ thuật này, một CMS tự viết trên Next 15 + Prisma) và kiwi.beru.io.vn (Kiwi, app quản lý nợ và chia bill nhóm bạn tôi build và đang chạy thật, xem lại bài giới thiệu ở Kiwi – App quản lý nợ và chia tiền nhóm). Vì công việc viết blog này liên quan trực tiếp tới việc web có "đọc được" bởi agent hay không — robots.txt, llms.txt, sitemap.xml, .well-known — tôi có thói quen fetch những file này trên chính site mình để kiểm tra. beru.io.vn thì tôi đã kiểm nhiều lần. Nhưng kiwi.beru.io.vn, sản phẩm tôi dành nhiều công sức nhất, thì chưa bao giờ tôi tự hỏi: mấy file đó có thật sự tồn tại không?
Câu trả lời, sau khi gọi trực tiếp bằng công cụ fetch của agent đang viết bài này: không. Không phải "không có" theo nghĩa 404 tử tế. Mà là mọi request tới các đường dẫn đó đều nhận về y hệt nội dung trang đăng nhập.
Bằng chứng: gọi từng URL một, ghi lại nguyên văn
Trước tiên, để có đối chứng, tôi gọi trang chủ thật của Kiwi. Đây là nội dung raw (markdown hoá từ HTML) tôi nhận được khi fetch https://kiwi.beru.io.vn/:
title: Kiwi — Ghi nợ nhẹ tênh, nhắc nhẹ nhàng
...
8.247
Người dùng
41.8k
Khoản nợ ghi nhận
94.3%
Tất toán đúng hạn
63
Nhóm bạn bè
Đây là trang marketing thật, có mascot, có phần tính năng (Dashboard thông minh, Chia bill nhóm, Nhắc nhẹ nhàng), có các con số 8.247 người dùng / 41.8k khoản nợ / 94.3% tất toán đúng hạn / 63 nhóm bạn bè hiển thị ngay trên hero. Tôi nói rõ: đây là con số trang chủ TỰ hiển thị, tôi không có cách nào đối chiếu với database thật của Kiwi để xác nhận đây là số liệu sống hay là copy tĩnh — mục đích ở đây là chứng minh trang chủ hoạt động bình thường, để phân biệt với phần lỗi phía dưới.
Giờ tới phần lệch pha. Tôi gọi https://kiwi.beru.io.vn/robots.txt — đường dẫn mà theo Robots Exclusion Standard, mọi crawler tử tế (Googlebot, GPTBot, ClaudeBot, PerplexityBot...) đều gọi trước khi crawl bất cứ thứ gì. Kết quả trả về, nguyên văn phần đầu:
meta-description: Đăng nhập vào Kiwi để quản lý nợ và chia bill nhóm bạn bè.
...
title: Đăng nhập · Kiwi
...
# Kiwi
Ghi nợ nhẹ tênh, nhắc nhẹ nhàng
## Chào mừng trở lại
Đăng nhập để tiếp tục quản lý nợ của bạn
Đăng nhập bằng Google
Đây là HTML của trang đăng nhập, không phải file robots.txt. Tôi nghĩ có thể do tool fetch của tôi bị redirect nhầm, nên thử tiếp https://kiwi.beru.io.vn/llms.txt — cùng kết quả, y hệt từng dòng meta tag, y hệt tiêu đề "Đăng nhập · Kiwi". Thử https://kiwi.beru.io.vn/sitemap.xml — vẫn là trang đăng nhập đó, không phải XML. Thử https://kiwi.beru.io.vn/manifest.json — vẫn vậy, không phải JSON.
Để loại trừ khả năng đây là bug riêng của bốn cái tên file cụ thể, tôi gọi một đường dẫn bịa hoàn toàn ngẫu nhiên: https://kiwi.beru.io.vn/this-path-does-not-exist-abc123. Kết quả: cùng tiêu đề "Đăng nhập · Kiwi", cùng nội dung "Chào mừng trở lại". Không phải trang 404 — tool fetch của tôi không báo lỗi HTTP nào cho bốn request này (robots.txt, llms.txt, sitemap.xml, manifest.json, cộng cả path bịa) trong khi nó có báo lỗi rõ ràng cho hai request khác mà tôi sắp nói tới ngay sau đây.
Đối chứng cuối: tôi gọi https://kiwi.beru.io.vn/.well-known/api-catalog và https://kiwi.beru.io.vn/.well-known/nonexistent-test. Cả hai lần, công cụ fetch trả lỗi rõ ràng: "The page returned a 404 client error". Đây là hành vi hoàn toàn khác — một 404 thật, không phải trang đăng nhập giả trang.
Tóm gọn bằng bảng:
| Đường dẫn | Kết quả nhận được |
|---|---|
/ | Trang chủ marketing thật (200) |
/robots.txt | HTML trang đăng nhập, không phải robots.txt |
/llms.txt | HTML trang đăng nhập, không phải llms.txt |
/sitemap.xml | HTML trang đăng nhập, không phải XML |
/manifest.json | HTML trang đăng nhập, không phải JSON |
/this-path-does-not-exist-abc123 | HTML trang đăng nhập (path bịa hoàn toàn) |
/.well-known/api-catalog | 404 thật |
/.well-known/nonexistent-test | 404 thật |
Đây không phải lỗi ngẫu nhiên của một file. Đây là hành vi nhất quán: mọi path không khớp route nào trong ứng dụng đều rơi vào cùng một nơi — trừ những gì nằm dưới /.well-known/.
Vì sao tôi tin đây là chuyện của Proxy, không phải Next.js "quên" file
Tôi không có quyền truy cập source code hay dashboard triển khai của Kiwi trong phiên làm việc này — tôi chỉ gọi được các URL công khai, và tôi sẽ không khẳng định chắc chắn cấu hình bên trong. Nhưng hành vi quan sát được khớp gần như hoàn hảo với một lớp lỗi rất nổi tiếng trong hệ sinh thái Next.js: matcher của tầng chặn request chạy trước khi routing thật của framework kịp xử lý.
Next.js gọi tầng này là Proxy kể từ bản 16 (trước đó gọi là Middleware). Tài liệu chính thức nói thẳng: "The middleware file convention is deprecated and has been renamed to proxy." Lý do đổi tên, theo đúng trang giải thích của Next.js:
"The reason behind the renaming of
middlewareis that the term 'middleware' can often be confused with Express.js middleware, leading to a misinterpretation of its purpose... The name Proxy clarifies what Middleware is capable of. The term 'proxy' implies that it has a network boundary in front of the app."
Next.js 16 phát hành ngày 21/10/2025 (theo đúng metadata publishedAt trên trang blog chính thức), kèm codemod tự động npx @next/codemod@canary middleware-to-proxy . để đổi tên file và hàm export. File middleware.ts cũ vẫn chạy được — tài liệu ghi rõ "The middleware.ts file is still available for Edge runtime use cases, but it is deprecated and will be removed in a future version" — nghĩa là một codebase viết từ thời Next 13-15 hoàn toàn có thể vẫn đang dùng middleware.ts mà chưa migrate, và cơ chế bên dưới (matcher, thứ tự chạy) không đổi giữa hai cái tên.
Cái quan trọng nhất, và là thứ giải thích chính xác những gì tôi thấy trên Kiwi, nằm ở thứ tự thực thi request mà Next.js công bố nguyên văn trong tài liệu proxy:
"Proxy will be invoked for every route in your project... The following is the execution order:
headersfromnext.config.jsredirectsfromnext.config.js- Proxy (
rewrites,redirects, etc.)beforeFiles(rewrites) fromnext.config.js- Filesystem routes (
public/,_next/static/,pages/,app/, etc.)afterFiles(rewrites) fromnext.config.js- Dynamic Routes (
/blog/[slug])fallback(rewrites) fromnext.config.js"
Đọc kỹ thứ tự này: Proxy đứng ở bước 3, Filesystem routes — nơi Next.js resolve app/robots.ts, app/sitemap.ts, hay file tĩnh public/robots.txt — đứng ở bước 5. Nếu Proxy trả về response (redirect hoặc rewrite) cho một request thay vì gọi NextResponse.next() để đi tiếp, request đó dừng lại ngay tại bước 3. Nó không bao giờ chạm tới bước 5. robots.txt có tồn tại thật trong source code hay không không còn quan trọng nữa — Proxy đã trả lời thay rồi.
Đây chính xác là mô tả khớp với những gì tôi quan sát: một ứng dụng có khả năng cao đang dùng Proxy (hoặc Middleware cũ) để gate toàn bộ site sau một lớp kiểm tra đăng nhập — kiểu logic rất phổ biến cho app dashboard cần auth ("chưa đăng nhập thì redirect về /login"). Nếu matcher của Proxy đó được viết kiểu bắt tất cả (/((?!_next/static|_next/image).*)" chẳng hạn) mà quên loại trừ tên file quy ước như robots.txt, sitemap.xml, manifest.json, llms.txt, thì mọi request tới các file đó sẽ bị Proxy nuốt mất trước khi Next.js kịp phục vụ bản thật — dù bản thật đó có được định nghĩa đúng chuẩn trong app/robots.ts hay không.
Tài liệu chính thức của Next.js thậm chí đưa ra sẵn một ví dụ matcher "negative matching" đúng như tình huống này cần, với comment giải thích ngay trong code:
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
*/
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
}
Chú ý: chính Next.js liệt kê favicon.ico, sitemap.xml, robots.txt là ba cái tên "metadata files" cần loại trừ tường minh khỏi matcher — nghĩa là đội ngũ Next.js coi đây là lỗi đủ phổ biến để đưa thẳng vào ví dụ chính thức, không phải edge case hiếm gặp. llms.txt thì không nằm trong danh sách đó, dễ hiểu vì nó không phải file convention chính thức của Next.js (tôi đã viết về việc llms.txt là spec cộng đồng, không phải chuẩn của framework, trong bài về llms.txt v2 trước đây) — nên một dev cẩn thận copy đúng ví dụ chính thức của Next.js vẫn có thể quên loại trừ llms.txt và manifest.json, hai cái tên không có trong bản mẫu.
Có một khả năng thứ hai tôi cũng cần nói thẳng để không đơn giản hoá quá mức: thay vì Proxy, hành vi này cũng có thể tới từ một route catch-all (app/[...slug]/page.tsx) hoặc logic gate ở root layout tự render UI đăng nhập cho mọi path không khớp, không đụng gì tới Proxy cả. Tôi không có quyền truy cập source code của Kiwi để phân biệt chắc chắn hai khả năng này. Điều tôi có thể khẳng định chắc chắn, vì tự tay gọi và nhận kết quả, là: (1) hiện tượng có thật và nhất quán trên nhiều path khác nhau, (2) nó khớp gần như từng chữ với mô tả execution order mà Next.js công bố công khai cho cơ chế Proxy, và (3) /.well-known/ là ngoại lệ duy nhất thoát được, thứ thường thấy khi một danh sách loại trừ có include .well-known (rất phổ biến để không chặn các file xác thực domain kiểu Apple Pay hay ACME) nhưng lại không include các file metadata khác.
Vì sao đây không chỉ là lỗi vặt: robots.txt trả về HTML thì crawler xử lý thế nào
Tôi tra thẳng tài liệu chính thức của Google về robots.txt để không đoán. Về trường hợp thân bài trả về không phải là robots.txt hợp lệ:
"if the content downloaded is HTML instead of robots.txt rules, Google will try to parse the content and extract rules, and ignore everything else."
Nói cách khác: Googlebot sẽ cố đọc trang đăng nhập của Kiwi như thể nó là robots.txt, tìm dòng nào bắt đầu bằng User-agent:, Allow:, Disallow:... Trang đăng nhập đó không có dòng nào như vậy, nên Google trích ra được đúng zero rule. Về mặt hệ quả crawl, điều này gần tương đương với không có robots.txt — tức mặc định cho phép crawl toàn bộ — nhưng "gần tương đương" khác với "giống hệt 404". Google cũng nói rõ hành vi khi gặp lỗi thật:
"Google's crawlers treat all 4xx errors, except 429, as if a valid robots.txt file didn't exist. This means that Google assumes that there are no crawl restrictions."
Vấn đề là Kiwi không trả 4xx cho /robots.txt — response tôi nhận được không có bất kỳ lỗi HTTP nào bật lên qua công cụ fetch, khác hẳn với hai lần gọi /.well-known/ bị bắt lỗi 404 rõ ràng. Một response "trông như thành công" nhưng nội dung sai hoàn toàn tạo ra vùng xám: với robots.txt thì hệ quả thực dụng có thể chấp nhận được (không có rule = không bị chặn), nhưng với llms.txt thì khác hẳn bản chất. llms.txt không có cơ chế "bỏ qua nếu không parse được" nào được chuẩn hoá — một agent tuân theo quy ước llms.txt (tôi đã nói ở bài trước, chuẩn này do cộng đồng, không phải W3C hay IETF) khi gọi /llms.txt và nhận về HTML của trang đăng nhập có thể coi nhầm đây LÀ nội dung mô tả trang web, đưa nguyên văn "Đăng nhập vào Kiwi để quản lý nợ và chia bill nhóm bạn bè" cùng nút "Đăng nhập bằng Google" vào context của nó, thay vì danh sách services/projects thật mà một mô tả llms.txt cần có. Không có gì "báo lỗi" ở đây cả để agent biết mà bỏ qua — nó là 200 OK trọn vẹn.
Tương tự với sitemap.xml: Search Console (và các agent đọc sitemap để index) mong đợi một tài liệu XML với namespace urlset. Nhận về HTML thay vì XML thường khiến parser XML thất bại thẳng thừng — đây thực ra còn là kết quả tốt hơn so với robots.txt (lỗi rõ ràng thay vì âm thầm hiểu sai), nhưng vẫn có nghĩa Kiwi chưa từng khai báo sitemap cho Google dù trang có nhiều URL công khai (/, /login, /privacy, /contact).
Đối chiếu với chính blog này: khoảng cách giữa lời khuyên và sản phẩm thật
Cái khiến tôi thấy chuyện này đáng viết không phải bản thân cái bug — matcher quên loại trừ vài tên file là lỗi rất con người, ai viết Proxy/middleware đủ lâu cũng có lúc dính. Cái đáng nói là khoảng cách giữa những gì tôi làm cho beru.io.vn và những gì tôi chưa từng làm cho Kiwi, dù cả hai đều do tôi build.
robots.txt thật của beru.io.vn, tôi fetch lại ngay trước khi viết đoạn này, có nội dung:
User-Agent: *
Allow: /
Allow: /api/openapi.json
Allow: /.well-known/
Disallow: /admin/
Disallow: /api/
User-Agent: GPTBot
User-Agent: OAI-SearchBot
User-Agent: ChatGPT-User
User-Agent: ClaudeBot
User-Agent: Claude-User
User-Agent: PerplexityBot
User-Agent: Google-Extended
User-Agent: Applebot-Extended
User-Agent: meta-externalagent
User-Agent: Bingbot
Allow: /
Allow: /api/openapi.json
Allow: /.well-known/
Disallow: /admin/
Disallow: /api/
Host: https://beru.io.vn
Sitemap: https://beru.io.vn/sitemap.xml
Đây là robots.txt viết tay, liệt kê tường minh từng tên crawler AI (GPTBot của OpenAI, ClaudeBot và Claude-User của Anthropic, PerplexityBot, Google-Extended, Applebot-Extended của Apple, meta-externalagent của Meta, cộng Bingbot) thay vì chỉ dùng User-Agent: *. llms.txt của beru.io.vn cũng là thật, tôi fetch được nội dung đầy đủ, mở đầu:
# Beru
> Full-stack developer based in Ho Chi Minh City, crafting performant web applications with Vue, React, and Next.js. Currently building AI-powered products at EasyAI.
Every URL below has a markdown version: prefix its path with `/md`, for
example https://beru.io.vn/md/blog/some-post. To read the whole site in one
request, fetch [https://beru.io.vn/llms-full.txt](https://beru.io.vn/llms-full.txt).
## Services
...
## Projects
...
File này liệt kê đủ bốn service (bao gồm Fullstack Development, đúng loại dự án như Kiwi) và gần hai chục project, cộng cơ chế mirror markdown qua prefix /md. Đây là hạ tầng "agent-readable" mà tôi đầu tư nghiêm túc cho blog — cùng lúc, sản phẩm thật, thứ có người dùng thật trả tiền chú ý bằng thời gian của họ, lại không có nổi một robots.txt tồn tại đúng nghĩa.
Điều này không phải để tự trách bản thân trên blog của chính mình cho vui — nó là một quan sát có giá trị thực dụng: rất dễ dồn hết công sức "làm chuẩn" vào nơi mình viết VỀ chuẩn, và quên áp dụng đúng chuẩn đó vào nơi thực sự cần — sản phẩm có traffic, có khả năng bị AI crawler quét, có khả năng cần index đúng trên Google. Một dev khác đọc bài llms.txt hay Content-Signal của tôi trước đây hoàn toàn có thể rơi vào chính cái bẫy tôi vừa tự bắt được trên sản phẩm của mình.
Vì sao chuyện này đáng quan tâm hơn vào lúc này
Tôi không muốn phóng đại: cơ chế matcher này đã tồn tại từ thời Middleware ổn định (Next.js 12.2, theo bảng version history chính thức), không phải chuyện mới tinh trong tuần này. Nhưng áp lực để dọn dẹp robots.txt đang tăng thật, không phải tôi tự nghĩ ra. Cloudflare — CDN mà cả beru.io.vn lẫn kiwi.beru.io.vn đang chạy phía sau, tôi resolve DNS và thấy cả hai domain trả về cùng dải IP 104.21.33.162 / 172.67.164.198 (dải anycast quen thuộc của Cloudflare, AS13335) — đang chuẩn bị đổi mặc định xử lý AI crawler kể từ 15/9/2026, chia crawler thành ba nhóm Search / Agent / Training thay vì một công tắc "chặn AI bot" duy nhất, theo đúng công bố của họ: "Training and Agent will be blocked by default on the pages that display ads, while Search will remain allowed by default." Tôi tra kỹ phạm vi áp dụng để không nói sai: thay đổi mặc định này, theo nguyên văn, chỉ áp dụng "for all new domains onboarding to Cloudflare" — tức domain cũ như beru.io.vn hay kiwi.beru.io.vn, vốn đã onboard từ trước, sẽ không tự động bị đổi default vào ngày 15/9. Nhưng đó không phải lý do để yên tâm — nó chỉ có nghĩa là gánh nặng dọn robots.txt cho domain cũ vẫn nằm nguyên trên vai người vận hành, không có Cloudflare lo hộ.
Cũng theo tài liệu Cloudflare, cơ chế managed robots.txt của họ (chèn sẵn dòng Content-Signal: search=yes,ai-train=no,use=reference cho khách hàng đã bật tính năng này) chỉ áp dụng "for all customers who have already enabled managed robots.txt". robots.txt thật của beru.io.vn mà tôi trích ở trên không có dòng Content-Signal nào — nghĩa là tôi đang tự viết tay robots.txt, không dùng tính năng managed của Cloudflare, một chi tiết đáng để tôi note lại cho một bài khác, không lạc đề ở đây.
Sửa thế nào — dựa trên đúng API Next.js công bố, không đoán
Vì tôi không truy cập được source Kiwi, phần này tôi viết dưới dạng hướng dẫn tổng quát cho bất kỳ ai đang dùng Proxy/Middleware để gate site kiểu tương tự, dựa hoàn toàn vào tài liệu chính thức tôi đã trích ở trên, không phải hướng dẫn riêng cho Kiwi.
Cách thứ nhất, sửa tại nguồn: thêm các tên file quy ước vào negative matcher, đúng theo mẫu Next.js đã công bố, mở rộng thêm những cái tên Next.js không liệt kê sẵn:
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|llms\\.txt|llms-full\\.txt|manifest\\.json|\\.well-known).*)',
],
}
Cách thứ hai, dùng đúng file convention của Next.js thay vì tự viết static file trong public/: app/robots.ts xuất một object Robots, ví dụ tối thiểu theo đúng tài liệu:
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: { userAgent: '*', allow: '/', disallow: '/private/' },
sitemap: 'https://kiwi.beru.io.vn/sitemap.xml',
}
}
Nhưng cần nhớ: file convention này chỉ được resolve ở bước 5 (Filesystem routes) theo đúng execution order tôi trích ở trên — nếu Proxy ở bước 3 đã trả response trước, việc có định nghĩa app/robots.ts đúng chuẩn tới đâu cũng vô nghĩa, request không bao giờ đi tới đó. Nói cách khác: sửa matcher là điều kiện cần, sửa/thêm file convention chỉ là điều kiện đủ. Thiếu vế đầu, vế sau vô tác dụng — đây chính xác là khả năng tôi nghĩ đang xảy ra trên Kiwi.
Với llms.txt, vì đây không phải file convention của Next.js, cách làm thực tế là thêm một Route Handler tường minh (app/llms.txt/route.ts) trả về text/plain, và đảm bảo path đó cũng nằm trong danh sách loại trừ của matcher — hai việc tách biệt, thiếu việc nào cũng hỏng.
Checklist để tự kiểm tra site của bạn
Nếu site của bạn cũng dùng Proxy hoặc Middleware để gate một phần hay toàn bộ ứng dụng phía sau đăng nhập, đây là năm request tôi vừa dùng để bắt lỗi trên Kiwi, bạn có thể lặp lại trong một phút:
- Gọi
/robots.txt— xem Content-Type trả về có phảitext/plainkhông, nội dung có đúng cú phápUser-agent:/Allow:/Disallow:không. - Gọi
/sitemap.xml— kiểm tra có phải XML hợp lệ, có namespaceurlsetkhông. - Gọi một path hoàn toàn bịa, ví dụ
/xyz-khong-ton-tai-123— nếu nó trả về y hệt nội dung như trang chủ hay trang login thay vì trang 404 riêng, đó là dấu hiệu matcher của bạn đang bắt tất cả. - So sánh với
/.well-known/bat-ky-gi— nếu path này 404 đúng còn ba cái trên thì không, gần như chắc chắn vấn đề nằm ở danh sách loại trừ của matcher, không phải do thiếu file. - Nếu bạn có publish
llms.txt, lặp lại đúng bốn bước trên với đường dẫn đó — đây là file dễ bị bỏ quên nhất vì không nằm trong bất kỳ ví dụ chính thức nào của framework.
Năm bước này không cần công cụ gì đặc biệt — curl, hoặc bất kỳ HTTP client nào, thậm chí gõ thẳng URL lên trình duyệt và view-source cũng đủ để thấy sự khác biệt giữa một file thật và một trang được render nhầm.
Những gì tôi chưa kiểm chứng được
Để người biên tập biết chỗ nào cần soi kỹ trước khi publish, tôi liệt kê rõ những gì bài này KHÔNG khẳng định chắc chắn:
- Tôi không biết Kiwi đang chạy phiên bản Next.js nào, và do đó không biết cơ chế chặn là
middleware.ts(tên cũ) hayproxy.ts(tên mới từ bản 16). Bài chỉ dựa trên hành vi quan sát được từ bên ngoài. - Tôi không có quyền truy cập source code hay dashboard triển khai của Kiwi, nên không thể khẳng định 100% nguyên nhân là matcher của Proxy/Middleware chứ không phải một route catch-all hay logic ở root layout tạo ra cùng hiệu ứng.
- Tôi suy ra các request
/robots.txt,/llms.txt,/sitemap.xml,/manifest.jsontrả về HTTP 200 dựa trên việc công cụ fetch không báo lỗi cho các request này (trong khi nó báo lỗi 404 rõ ràng cho hai request/.well-known/) — tôi không tự đọc header HTTP thô để xác nhận mã trạng thái chính xác. - Tôi không phân biệt được đây là redirect (3xx) hay rewrite (200 tại chỗ, giữ nguyên URL) — cả hai đều khớp với những gì tool fetch hiển thị cho tôi.
- Tôi không có quyền truy cập Google Search Console hay bất kỳ công cụ crawl-stats nào của Kiwi, nên không biết tác động thực tế lên index hiện tại ra sao — phần phân tích hệ quả trong bài dựa hoàn toàn trên tài liệu công khai của Google về cách họ xử lý robots.txt không hợp lệ, không phải dữ liệu quan sát trực tiếp từ crawl log của Kiwi.
- Tôi không kiểm tra được liệu kiwi.beru.io.vn có hiển thị quảng cáo hay không (không có, theo những gì tôi thấy trên trang) — chi tiết này chỉ liên quan gián tiếp tới phần nói về default mới của Cloudflare cho domain mới, không phải trọng tâm bài.
Việc dừng lại kiểm tra một sản phẩm thật, thay vì chỉ tổng hợp tin tức, đúng là chậm hơn — nhưng nó cho ra một danh sách sửa lỗi có thể chạy curl để verify ngay hôm nay, thay vì một bài tóm tắt spec mà không ai áp dụng được vào codebase thật của mình.
Viết và kiểm chứng bởi Nguyễn Hoàng Lộc (Beru), TP. Hồ Chí Minh
