Tran Manh Tuan

Case study — Truy vết HTTP 500 chỉ xảy ra với Googlebot, xuyên Next.js, Cloud Run và hosting edge

Case study này đã lược bỏ mã nguồn thuộc sở hữu khách hàng, thông tin repository nội bộ, mã định danh cloud project, timestamp vận hành, trao đổi nguyên văn với nhà cung cấp và dữ liệu mật của khách hàng. Nội dung giữ lại là phương pháp điều tra, chuỗi lỗi ở mức kỹ thuật và bài học vận hành.

Bối cảnh

Một website production cho khách hàng doanh nghiệp Nhật: Next.js dùng App Router, chạy trên Cloud Run, traffic công khai phục vụ qua Firebase Hosting.

Một đặc điểm của mô hình này định hình toàn bộ quá trình điều tra. Đội dự án quan sát trực tiếp được ứng dụng và Cloud Run. Tầng edge phục vụ domain công khai thì phần lớn nằm ngoài vùng quan sát đó — không có quyền truy cập trực tiếp để xem toàn bộ hành vi bên trong của nó.

GooglebotFirebase Hostingtầng edge công khaiCloud Runorigin Next.jsrewrite
Domain công khai đi qua một tầng edge mà ứng dụng không quan sát trực tiếp được. Vì vậy một request thành công ở origin không nói lên điều gì về thứ Googlebot nhận được trên domain công khai.

Khoảng trống đó chính là nơi sự cố xảy ra.

Vấn đề

Search visibility giảm mạnh trong thời gian ngắn. Bản thân website vẫn hoạt động bình thường với người dùng thật.

Tổ hợp đó loại ngay những cách giải thích thông thường. Đây không phải vấn đề traffic, cũng không phải vấn đề nội dung — đây là vấn đề hạ tầng, riêng ở cách crawler được phục vụ.

Triệu chứng

Bốn đặc điểm khiến case này khó truy vết, và đáng nêu ra vì mỗi đặc điểm vô hiệu hoá một phản xạ debug quen thuộc:

Lỗi vô hình với người dùng và với monitoring thông thường. Browser và các bot khác nhận HTTP 200. Uptime check kiểu phổ thông không thấy gì bất thường.

Lỗi không để lại dấu vết ở nơi ai cũng tìm trước tiên. Các page request lỗi của Googlebot không bao giờ tới origin, nên Cloud Run log không có stack trace và không có 5xx tương ứng.

Nguyên nhân và hậu quả cách nhau về thời gian. Regression phía ứng dụng đã lên từ trước thời điểm sự cố lộ ra khá lâu. Vì vậy đối chiếu với deploy gần nhất dẫn sai hướng.

Đây là chuỗi lỗi nhiều tầng, không phải một lỗi đơn lẻ. Chỉ nhìn ứng dụng thì thấy một response hỏng ở /robots.txt. Chỉ nhìn SEO thì thấy Googlebot ngừng crawl. Giá trị nằm ở chỗ nối được hai hiện tượng đó thành một chuỗi nhân quả duy nhất.

Quá trình điều tra

Nguyên tắc xuyên suốt: mỗi bước phải loại trừ được một nhóm giả thuyết, không chỉ gom thêm bằng chứng cho giả thuyết đang nghiêng về nhất.

Điều này quan trọng hơn vẻ ngoài của nó. Giả thuyết ban đầu nghiêng hẳn về hạ tầng, và nếu cứ gom bằng chứng ủng hộ nó thì kết quả sẽ là một câu trả lời nghe chắc chắn nhưng thiếu — regression phía ứng dụng sẽ bị bỏ sót hoàn toàn.

Các phép thử chẩn đoán

1. Ma trận hai chiều để định vị tầng gây lỗi

Thay vì rà từng cấu hình một, tôi test chéo giữa đường đi của request và user agent:

Đường đi của requestUA thườngUA Googlebot
Qua domain công khai (edge)200500
Trực tiếp vào origin Cloud Run200200

Lỗi 500 toàn site mà Googlebot nhìn thấy chỉ tồn tại tại giao điểm giữa public edge và Googlebot. Điều đó loại các page request thông thường của ứng dụng khỏi vai trò nguồn lỗi trực tiếp — nhưng chưa giải thích được điều kiện nào kích hoạt hành vi ở tầng edge.

2. So sánh nhiều crawler để tìm đúng ranh giới

CrawlerCó chuỗi "Googlebot" trong UASau khi sự cố bắt đầu
Googlebotcóngừng crawl nội dung trang
AdsBot-Googlekhôngvẫn hoạt động
bingbotkhôngvẫn hoạt động
DuckDuckBotkhôngvẫn hoạt động

AdsBot-Google là dòng cho nhiều thông tin nhất: cũng là Google, cũng crawl, nhưng không bị ảnh hưởng. Vậy đây không phải "bot bị chặn", cũng không phải "site chết". Hành vi lỗi bám theo cách các request mang chuỗi Googlebot trong user agent được xử lý trên đường đi công khai.

3. Dựng timeline từ log thay vì suy đoán

Hoạt động crawler diễn ra đều đặn tới một thời điểm rồi dừng tuyệt đối, trong khi các bot khác tiếp tục bình thường. Một snapshot tĩnh sẽ không cho thấy điều đó. Điểm gãy sắc nét trên timeline cho thấy một ngưỡng trạng thái đã bị vượt qua, chứ không phải suy giảm dần.

4. Site đối chứng

Tôi chạy cùng bộ phép thử trên một website khác cùng kiến trúc. Nó không có hành vi 500 với Googlebot, qua đó loại bỏ giả thuyết "sự cố diện rộng toàn nền tảng".

5. Từ triệu chứng ở edge tới dependency thật sự

Sau khi loại các page request thông thường, tôi chuyển sang các dependency mà Googlebot kiểm tra trước khi crawl — /robots.txt đầu tiên. Chính ở đây chuỗi lỗi lộ ra.

Nguyên nhân gốc

Một regression phía ứng dụng và một cơ chế khuếch đại ở hosting edge. Không nửa nào tự nó giải thích được sự cố; chỉ khi ghép lại mới ra chuỗi lỗi hoàn chỉnh.

Phía ứng dụng. Sau khi site được nâng cấp sang luồng đa ngôn ngữ, các dot-path lạ như /robots.txt không còn rơi về 404 thuần nữa. Một phần logic i18n và 404 dùng runtime-only request API trong ngữ cảnh render tĩnh, khiến thứ lẽ ra là 404 bị đẩy thành HTTP 500. Đây là regression thật trong code, và tôi sửa trực tiếp phần này.

Tại hosting edge. Sau khi escalate lên nhà cung cấp cloud, phía họ xác nhận traffic của Googlebot đi qua một bước xác minh crawler bổ sung trước khi nội dung trang được crawl. Khi /robots.txt trả 5xx thay vì 404 hoặc 200 hợp lệ, bước xác minh đó thất bại và các page request của Googlebot nhận synthetic 5xx sinh ra tại edge — dù nội dung trang ở origin vẫn bình thường.

Regression phía ứng dụng/robots.txt trả 500 thay vì 404Bước xác minh crawler ở edgeđọc /robots.txt trước khi crawl nội dung trangGiai đoạn một — vẫn bình thườngcache còn hiệu lực, Googlebot crawl như thườngCache hết hiệu lựcGiai đoạn hai — synthetic 500 tại edgexác minh thất bại, page request của Googlebot nhận 5xxGooglebot100% page request thất bạiNgười dùng và bot khácbrowser, bingbot, DuckDuckBot: 200Crawl giảm rồi dừng hẳntrông như sự cố availability toàn site
Hai đặc điểm khiến case này khó truy vết. Tuổi thọ cache tách regression khỏi thời điểm sự cố lộ ra, nên đối chiếu với deploy gần nhất sẽ dẫn sai hướng. Và nhánh bên phải không bao giờ lỗi — đó là lý do site trông vẫn khoẻ mạnh với tất cả mọi người trừ Googlebot.

Cùng trình tự đó diễn đạt bằng lời:

  1. /robots.txt bắt đầu trả 500. Lỗi nằm sâu trong luồng 404 và i18n nên không ai để ý — trang thông thường vẫn trả 200.
  2. Giai đoạn một: cache còn hiệu lực. Googlebot vẫn crawl bình thường, vì chưa buộc phải đánh giá lại trạng thái của robots.txt.
  3. Giai đoạn hai: đánh giá lại, và khuếch đại. Khi cache hết hiệu lực, edge bắt đầu trả synthetic 500 cho các page request của Googlebot.
  4. Googlebot giảm rồi ngừng crawl. Từ góc nhìn của nó, site đang có vấn đề availability ở quy mô toàn site.
  5. Khắc phục cả phần triệu chứng lẫn phần nguyên nhân.

Độ trễ hai giai đoạn đó chính xác là lý do việc đối chiếu với deploy gần nhất làm lệch hướng điều tra: regression và sự cố nhìn thấy được cách nhau đúng bằng tuổi thọ của một cache entry.

Điều làm case này khó chịu là: bug phía ứng dụng có thật, nhưng triệu chứng nặng nhất của nó lại xuất hiện ở một tầng khác và chỉ với đúng một crawler. Dừng điều tra ở bất kỳ tầng đơn lẻ nào cũng sẽ cho một kết luận không đầy đủ.

Khắc phục

  • Thêm public/robots.txt tĩnh, để file này không còn phụ thuộc vào logic routing hay i18n.
  • Sửa root not-found để nó không phụ thuộc vào runtime path của i18n.
  • Ràng buộc segment lạ để các dot-path không quen thuộc trả về 404 đúng chuẩn.

Hai nửa này làm hai việc khác nhau: file tĩnh là biện pháp giảm thiểu, cầm máu ngay lập tức; sửa routing mới là chữa thật.

Kết quả

  • Phục hồi. Ứng dụng trả lại đúng semantics cho các dot-path lạ, và production có một robots.txt tĩnh an toàn. Googlebot fetch lại thành công gần như ngay sau deploy, crawl trang hồi phục trong vòng vài phút.
  • Xác nhận từ nhà cung cấp. Phía cloud provider xác nhận hành vi khuếch đại lỗi ở hosting edge và thừa nhận nó gây hiểu nhầm khi troubleshoot crawler.
  • Giá trị cho dự án. Chấm dứt tình trạng đổ qua đổ lại giữa SEO, frontend và hạ tầng, bằng cách thay ý kiến bằng một chuỗi lỗi có bằng chứng từ đầu đến cuối.
  • Giá trị vượt ra ngoài dự án. Bài học này được dùng để thiết kế guardrail cho các site khác cùng kiến trúc, đặc biệt ở các route phục vụ SEO như /robots.txt và /sitemap.xml.

Hệ thống phòng ngừa

Sự cố phơi ra một khoảng trống giám sát: không có gì theo dõi các route tồn tại riêng để phục vụ crawler, nên một lỗi ở đó có thể tồn tại rất lâu mà không ai biết. Ba lớp, mỗi lớp bịt điểm mù của lớp kia:

LớpCơ chếBắt được gìĐiểm mù
Ngăn chặnSmoke test trong CI/CD sau mỗi deploy, kiểm /, /robots.txt và /sitemap.xml bằng user agent GooglebotLỗi do code hoặc deploy, chặn ngay tại pipelineKhông chạy khi không có deploy
Chủ độngSynthetic monitoring định kỳ qua domain công khai với user agent tuỳ chỉnh, kiểm cả status và nội dungMọi nguyên nhân, kể cả thay đổi hạ tầng — và cả trạng thái "không còn traffic nào"Dùng UA giả lập, không phải crawler thật
Quan sátLog-based alert trên request log của hosting edgeCrawler thật đang thực sự nhận gì tại edgeChỉ thấy được khi vẫn còn request

Hai quyết định thiết kế đáng nêu rõ. Thứ nhất, các phép kiểm phải đi qua domain công khai, không phải URL origin — test origin sẽ bỏ qua đúng tầng đã gây ra sự cố. Thứ hai, chúng validate nội dung body, không chỉ status code: một robots.txt trả 200 nhưng chứa Disallow: / còn nguy hiểm hơn một file trả 500.

Bài học rút ra

Sự vắng mặt của dữ liệu cũng là dữ liệu — nhưng chỉ khi có thứ để đối chiếu. Thứ phá được case này không phải một stack trace. Đó là việc một loại traffic biến mất khỏi origin trong khi mọi luồng khác vẫn bình thường.

Một phép thử được thiết kế tốt hơn mười phép thử tuỳ tiện. Ma trận hai chiều định vị đúng tầng chịu trách nhiệm cho một triệu chứng toàn site chỉ trong vài câu lệnh.

Một lỗi nhỏ trở thành lỗi lớn khi nó nằm trên route phục vụ crawler. Thiếu robots.txt thì vô hại. /robots.txt trả 5xx thì không.

Phải dám sửa kết luận ban đầu của chính mình. Giả thuyết đầu tiên của tôi nghiêng hẳn về hạ tầng. Điều tra tử tế cho thấy regression phía ứng dụng là một mắt xích thiết yếu trong chuỗi, và báo cáo cuối cùng phải phản ánh trung thực cả hai nửa, chứ không phải nửa tôi mong tìm thấy.

Một sự cố được xử lý tốt kết thúc bằng một hệ thống, không phải một bản vá. Giá trị bền vững nằm ở checklist, guardrail và observability được bổ sung sau đó.

Kỹ năng thể hiện qua case này

Điều tra sự cố production · debug routing và i18n của Next.js · GCP — Cloud Run, Cloud Logging, Monitoring · troubleshoot hosting edge và CDN · hạ tầng SEO và hành vi crawler · thiết kế observability và alert · escalate với nhà cung cấp và trình bày kỹ thuật