Crawler 192 Halaman, Satu 404, dan Sitasi yang Salah Sasaran
Audit tautan internal lewat crawler Playwright: 192 halaman dirayapi, satu dynamic404, dan rujukan next.js#58421 yang ternyata PR polyfill.
Ringkasan
Crawler ngecek 192 halaman, cuma satu 404 dinamis gara-gara slug ada tanda kutip satu. Rujukan issue di commit ternyata salah sasaran, akar masalahnya sih double-encoding dari encodeURIComponent. Pelajarannya, referensi di catatan kerja itu cuma hipotesis, mesti dicek dulu sebelum dipercaya.
Terminal menampilkan ringkasan yang tampak sempurna: 192 halaman dirayapi, hardBroken 0, redirects 0. Baris terakhir menyisakan satu angka: dynamic404 = 1, dengan jalur /pariwisata/sarana-olahraga/k'mang-futsal. Tanda kutip satu di slug itu adalah petunjuk pertama bahwa halamannya memang ada, yang rusak adalah cara URL-nya dibentuk.
Kebiasaan lama saat melihat hasil audit begitu ini: buka pesan commit, ikuti referensi yang dicantumkan. Pesan commit crawler ini menunjuk issue nomor 58421 di repo Next.js sebagai rujukan masalah double-encoding. Cek lewat API GitHub, nomor itu ternyata pull request yang sudah ditutup dan isinya menambahkan polyfill metode-metode penyortiran array. Tidak ada satu baris pun yang berkaitan dengan encoding URL. Sitasi di catatan kerja salah sasaran, dan kalau diikuti begitu saja, perbaikan yang dirancang akan menargetkan masalah yang tidak pernah ada.
Akar masalah yang benar-benar terverifikasi
Dua sumber yang bisa dipertanggungjawabkan menggantikan rujukan palsu itu. Pertama, dokumentasi MDN untuk encodeURIComponent menjelaskan fungsi ini meng-escape karakter dalam set yang lebih besar, dan encoding yang diulang pada string yang sudah ter-encode mengubah tanda persen menjadi kode persen lagi, sehingga urutan kode milik tanda kutip satu berubah menjadi kode bersarang yang tak dikenal server [6]. Kedua, issue terbuka Next.js #89879 yang mendokumentasikan URL permintaan internal di-serialize ulang secara salah sampai nilai parameter yang memuat karakter pemisah kueri terpotong [5]. Kombinasi keduanya menjelaskan mengapa slug dengan karakter khusus bisa mendarat di 404 meski datanya ada di CMS. Interaksi spesifik di aplikasi ini antara encodeURIComponent di lapisan klien API dan penanganan parameter Next 14 tetap dicatat sebagai hipotesis kerja di ledger keputusan owner, bukan sebagai fakta yang sudah diverifikasi dari kode sumber.
Pola crawler: seed statis, discovery dinamis
Crawler di berkas routing.spec.ts panjangnya 58 baris. Empat belas rute statis publik dijadikan seed, sisanya ditemukan lewat antrian BFS dari atribut href setiap tautan di dalam domain. Pemanggilan goto mengembalikan main resource response sehingga status dan URL akhir bisa dibaca langsung [1], lalu evaluasi in-page mengekstrak semua href dari halaman yang sudah terbuka [1]. Tiga filter menjaga perayapan tetap di jalur: set visited mencegah halaman yang sama diambil dua kali, fragmen hash di URL dilewati karena tidak pernah menyentuh server, dan URL yang tidak berawalan domain aplikasi dibuang.
const resp = await page.goto(url, { waitUntil: "domcontentloaded" });
if (!resp) { broken.push(`${url} (no response)`); continue; }
if (resp.status() === 404 || resp.status() >= 500) broken.push(`${url} -> ${resp.status()}`);
if (resp.url() !== url && resp.status() >= 300 && resp.status() < 400)
redirects.push(`${url} -> ${resp.url()}`);Klasifikasi hasilnya dibagi dua dengan konsekuensi berbeda. hardBroken gagal keras lewat expect: statis yang pecah berarti kontrak routing dilanggar dan build harus merah. dynamic404 hanya dilaporkan: route dinamis bergantung data yang bisa berubah kapan saja, dan melaporkannya sebagai temuan lebih jujur daripada menggagalkan seluruh suite karena satu konten lama. Setiap halaman hardBroken juga meninggalkan bukti screenshot lewat fixture evidence.
Kenapa peramban, bukan fetch statis
Alat pemindai tautan berbasis HTTP cukup untuk sisi server, tetapi melewatkan satu lapisan: Next.js secara otomatis me-prefetch rute yang dihubungkan komponen Link saat masuk viewport, sementara rute dinamis melewatkan prefetch itu atau hanya sebagian [3]. Perilaku yang dialami pengguna tidak identik dengan daftar URL di sitemap. Protokol sitemap sendiri menuntut XML dengan entitas ter-escape, satu host per berkas, dan <loc> wajib untuk tiap URL [2]; praktik yang paling sering diabaikan menurut dokumentasi Google justru batas ukuran, lokasi berkas, dan URL mana yang boleh masuk [4]. Crawler peramban menutup celah itu karena menyusuri halaman persis seperti yang dilihat pengunjung.
Angka akhir sesi ini: 192 halaman, 0 rusak keras, 1 gagal dinamis, 0 pengalihan. Satu temuan cukup untuk membuka lapisan berikutnya, perbaikan encoding dijadwalkan terpisah dari slice ini. Pelajaran yang bertahan lebih lama dari angkanya: referensi di catatan kerja adalah hipotesis, bukan bukti, dan crawl enam digit detik sekali jalan lebih murah daripada satu pengguna yang menemukan 404 lebih dulu.
Sources: [1] dokumentasi Playwright untuk class Page [2] spesifikasi protokol sitemaps.org [3] dokumen resmi Next.js tentang linking dan navigating [4] panduan build sitemap di Google Search Central [5] issue #89879 di repo vercel/next.js [6] referensi MDN untuk encodeURIComponent