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ó.
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 request | UA thường | UA Googlebot |
|---|---|---|
| Qua domain công khai (edge) | 200 | 500 |
| Trực tiếp vào origin Cloud Run | 200 | 200 |
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
| Crawler | Có chuỗi "Googlebot" trong UA | Sau khi sự cố bắt đầu |
|---|---|---|
| Googlebot | có | ngừng crawl nội dung trang |
| AdsBot-Google | không | vẫn hoạt động |
| bingbot | không | vẫn hoạt động |
| DuckDuckBot | không | vẫ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.
Cùng trình tự đó diễn đạt bằng lời:
/robots.txtbắ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.- 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. - 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.
- 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.
- 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.txttĩ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.txttĩ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.txtvà/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ớp | Cơ chế | Bắt được gì | Điểm mù |
|---|---|---|---|
| Ngăn chặn | Smoke test trong CI/CD sau mỗi deploy, kiểm /, /robots.txt và /sitemap.xml bằng user agent Googlebot | Lỗi do code hoặc deploy, chặn ngay tại pipeline | Không chạy khi không có deploy |
| Chủ động | Synthetic monitoring định kỳ qua domain công khai với user agent tuỳ chỉnh, kiểm cả status và nội dung | Mọ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át | Log-based alert trên request log của hosting edge | Crawler thật đang thực sự nhận gì tại edge | Chỉ 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
