D
P
0
← Semua artikel Read in English

SEO Teknis, Redirect & Migrasi

Title Rank Math Cuma Nama Situs dan Canonical Tidak Ada di URL `add_rewrite_rule`: Halaman Virtual Tanpa Queried Object

· · 8 menit baca
Title Rank Math Cuma Nama Situs dan Canonical Tidak Ada di URL `add_rewrite_rule`: Halaman Virtual Tanpa Queried Object

SEO lead klien mengirim permintaan pendek pada 19 Agustus: tag canonical empat halaman hub role, halaman yang mengumpulkan profil orang per role, minta di-update ke halaman induknya di /people/ untuk sekarang, karena keempatnya masih kosong. Kata kerjanya update, tapi respons keempat URL itu, yang saya periksa sebelum perbaikan apa pun dipasang, tidak memuat string canonical satu kali pun. Tidak ada tag yang bisa diperbarui. Permintaan itu sebenarnya penambahan.

Sheet meta dari SEO lead yang sama, bertanggal sehari sesudahnya, membawa gejala kedua. Dari 30 baris title dan description di sheet itu, 28 URL tidak punya post di belakangnya, dan title ke-28 halaman itu merender nama situs saja. Bentuknya sama dengan urusan canonical tadi: halaman virtual, tidak ada queried object, dan kode tema jadi satu-satunya tuas.

Kalau sebuah URL punya post atau term, meta Rank Math masih bisa ditulis massal dari luar editor, dan itu cerita yang sudah saya tulis terpisah. Tulisan ini tentang URL yang tidak punya objek apa pun untuk ditempeli meta.

Rute yang tidak membawa objek apa pun

Hub role itu halaman virtual. Rutenya didaftarkan di tema lewat add_rewrite_rule, dan aturan itu hanya mengisi dua query var, satu penanda tampilan dan satu slug role, tanpa menunjuk post apa pun.

// Disederhanakan. Nama query var dan path sudah diganti.
add_rewrite_rule(
    '^people/role/([^/]+)/?$',
    'index.php?theme_role_view=1&theme_role_slug=$matches[1]',
    'top'
);

Template hub menjalankan query orang per role-nya sendiri, tetapi dari sisi WordPress permintaan ini tidak pernah punya queried object. Rank Math tidak punya bahan untuk membangun canonical, jadi tidak mengeluarkan satu pun, dan karena tidak ada post, tidak ada pula metabox Rank Math untuk mengisinya dengan tangan. Sebuah tag SEO akhirnya butuh deploy kode.

Sheet meta tadi menceritakan hal yang sama dalam skala lebih besar. url_to_postid() mengembalikan 0 untuk ke-28 URL itu, hanya dua URL di sheet yang ternyata Page sungguhan, dan keluarga URL yang lain juga rute add_rewrite_rule. Metode update_post_meta massal yang saya pakai 18 Agustus menulis rank_math_title dan rank_math_description, kunci yang sama dengan yang ditulis importer Rank Math, dan metode itu tidak menjangkau satu pun dari URL ini. update_post_meta() butuh ID post, dan di sini ID-nya 0.

Angka 0 dari url_to_postid() sendiri baru petunjuk. Di situs lain, fungsi itu pernah mengembalikan 0 untuk URL yang post-nya ada, karena post_name post itu bersufiks -2 sementara URI dari plugin Permalink Manager tidak. Di kasus ini, angkanya didukung aturan rewrite yang memang terdaftar di kode tema.

Enam hub, nol orang, dan satu salah ketik

Tema mendefinisikan enam role dan daftar klien menyebut empat, jadi sebelum menulis kode saya ingin tahu dulu hub mana yang benar-benar kosong. Skrip pengukur pertama saya memanggil helper penghitung orang dengan nama yang tidak pernah ada di tema, dan pemanggilan itu saya bungkus function_exists(). Keenam hub terbaca kosong, dan angka itu nyaris saya laporkan sebagai temuan. Bentuknya kira-kira seperti ini:

// Disederhanakan. Nama helper sudah diganti.
// Nama yang salah ketik membuat function_exists() bernilai false,
// tidak ada galat, dan setiap hub dihitung 0.
$count = function_exists('theme_people_by_role_tag')
    ? count(theme_people_by_role_tag($key))
    : 0;
 
// Yang seharusnya: helper wajib ada, kalau tidak skrip berhenti.
if (!function_exists('theme_people_by_role')) {
    fwrite(STDERR, "helper tidak ditemukan\n");
    exit(1);
}
$count = count(theme_people_by_role($key));

Hitungan yang benar, dicocokkan langsung ke meta role mentah, menunjukkan dua hub berisi masing-masing 2 orang dan empat sisanya 0. Empat hub kosong itu persis empat URL di daftar klien. Di kode tema, function_exists() adalah pagar yang wajar. Di skrip pengukur, pagar yang sama menelan salah ketik dan mengembalikannya sebagai data, jadi aturan yang saya catat sesudahnya sederhana: pastikan helper-nya ada, dan hentikan skrip kalau tidak.

Canonical yang mengikuti isi hub

Klien menulis "untuk sekarang". Daripada mengunci empat slug di dalam kode, canonical-nya saya gerakkan oleh kosongnya hub. Hub yang mendapat orang pertamanya melepas canonical itu dengan sendirinya, dan hub yang kembali kosong mendapatkannya lagi, jadi "untuk sekarang" terpenuhi tanpa ada yang harus diingat untuk di-revert. Hub dianggap kosong kalau helper orang per role, yang mengambil post profil berstatus publish dengan meta role yang cocok, mengembalikan nol untuk role key yang diturunkan dari slug di URL. Hasilnya dimemo karena template hub menjalankan query yang sama.

Canonical-nya dipasang lewat filter rank_math/frontend/canonical di prioritas 20, yang untuk hub kosong mengembalikan URL halaman induk sambil mencatat bahwa dirinya sudah terpanggil. Fallback di wp_head prioritas 99 hanya mencetak canonical kalau filter itu tidak terpanggil, jadi keduanya tidak pernah tercetak bersamaan. Angka 99 dipilih karena Rank Math merender head-nya lebih awal, sehingga saat fallback berjalan, filter itu sudah terpanggil atau tidak akan pernah. Filter di bawah ini kutipan dari tema dengan nama yang diganti, sedangkan fallback-nya saya tulis ulang dalam bentuk paling ringkas.

// Disederhanakan. Nama fungsi dan path sudah diganti.
add_filter('rank_math/frontend/canonical', function ($canonical) {
    if (!theme_is_empty_role_hub()) return $canonical;
    theme_role_hub_canonical_fired(true);
    return home_url('/people/');
}, 20);
 
// Fallback: hanya mencetak kalau filter di atas tidak pernah terpanggil.
add_action('wp_head', function () {
    if (!theme_is_empty_role_hub() || theme_role_hub_canonical_fired()) return;
    echo '<link rel="canonical" href="' . esc_url(home_url('/people/')) . '" />' . "\n";
}, 99);

Fallback itu sengaja tidak memakai lewatan defined('RANK_MATH_VERSION') yang dipakai bagian lain di berkas yang sama. Lewatan itu membuat tema diam selama Rank Math aktif, padahal di tampilan ini Rank Math aktif tetapi tidak bisa diandalkan mencetak apa pun. Kalau nanti klien ingin keempat hub itu tetap terkunci ke halaman induk berapa pun isinya, uji jumlahnya tinggal diganti allow-list slug.

Verifikasi dengan fetch yang melewati cache di keenam hub menunjukkan empat hub kosong masing-masing mengeluarkan tepat 1 canonical ke halaman induk, sementara dua hub berisi mengeluarkan 0 dan tidak tersentuh. Sehari kemudian, robots.txt baru dipasang lewat berkas yang sama, dan pemeriksaan regresi sesudahnya memberi angka yang sama, dengan beranda 1 canonical dan title virtual yang dibahas di bawah tetap tampil.

Title dari peta request path

Commit canonical tadi tercatat 20 Agustus pukul 18.04 WIB, dan commit untuk title dan description menyusul pukul 20.12 di hari yang sama. Perbaikannya satu berkas baru di folder inc, di-require dari functions.php tepat sesudah berkas filter SEO yang memuat canonical tadi. Isinya peta 27 entri yang dikunci dengan request path huruf kecil berapit garis miring, bentuk yang sengaja mengikuti sheet klien supaya berkasnya tetap bisa di-diff terhadap sumbernya.

Angkanya 27, bukan 30. Dua URL yang ternyata Page sungguhan diberi post meta rank_math_title dan rank_math_description biasa, supaya orang di sisi klien tetap bisa menyuntingnya sendiri, dan satu baris lagi sengaja ditinggalkan karena alasan editorial. Hitungannya 30 dikurangi 2 dikurangi 1.

Sebelum dicocokkan, path diambil dari REQUEST_URI lewat parse_url, di-decode, dibuang garis miring di kedua tepinya, diubah ke huruf kecil, lalu dibungkus garis miring lagi. Decode-nya harus jalan lebih dulu karena salah satu slug di sheet memuat koma, dan klien HTTP yang meng-encode koma itu menjadi %2C akan meleset dari peta.

// Dari berkas meta virtual, komentar diterjemahkan.
$path = (string) parse_url($uri, PHP_URL_PATH);
if ($path === '') return $entry;
 
// Decode sebelum mencocokkan. Satu slug di sheet memuat koma, dan klien
// yang meng-encode koma itu akan meleset dari peta.
$path = rawurldecode($path);
$trimmed = trim($path, '/');
$path = ($trimmed === '') ? '/' : '/' . strtolower($trimmed) . '/';

Entri yang cocok dipasang ke filter rank_math/frontend/title dan rank_math/frontend/description di prioritas 20, dan keberadaan kedua filter itu saya pastikan dulu di Rank Math 1.0.273 yang terpasang sebelum kodenya dibangun. Sel kosong di sheet jatuh ke nilai yang dihitung Rank Math sendiri, bukan mengosongkan tag. Pencarian petanya dimemo, karena pencarian itu berjalan di setiap request front-end, termasuk 458 post yang tidak akan pernah cocok.

Harness untuk peta yang tidak bisa protes

Peta itu tidak punya layar editor. Kalau satu kuncinya bergeser, tidak ada yang protes di mana pun, dan halamannya diam-diam kembali merender nama situs polos. Karena itu peta ini punya harness sendiri: 11 kasus path, ditambah 11 asersi peta yang memeriksa kunci huruf kecil berapit garis miring, tidak ada sel kosong, tidak ada title di atas 60 karakter atau description di atas 160, title yang unik, dan tiga baris yang wajib absen.

Membangun harness itu memunculkan dua jebakan lagi. Tanpa ABSPATH terdefinisi, require berkas tema keluar dengan status 0 sebelum satu asersi pun berjalan, jadi harness tampak lulus tanpa mencetak apa pun. Penjaga ABSPATH yang lazim di awal berkas tema memanggil exit, dan exit tanpa argumen berstatus 0. Lalu Git Bash mengubah argumen berawalan garis miring menjadi path Windows, sehingga semua kasus yang seharusnya cocok gagal dan semua kasus yang seharusnya meleset justru lulus, untuk alasan yang salah.

Yang diterima dan yang masih terbuka

Satu celah sengaja diterima. Tema dulu mencetak set tag OG kedua di wp_head prioritas 5, sesudah Rank Math di prioritas 1, dan set itu akan menimpa deskripsi yang baru dipasang. Sesudah set kedua itu dibereskan, hub role tidak punya og:title maupun og:description sama sekali, karena Rank Math tidak mengeluarkannya untuk rute rewrite tanpa queried object. Sebelumnya nilainya hanya nama situs dan tagline. Nilai sungguhan saya serahkan ke SEO lead klien untuk ditulis di peta yang sama, bukan saya karang.

Tiga hal lain masih terbuka dengan klien. Hub kosong tetap menyajikan meta robots follow, index, dan karena canonical hanya petunjuk sedangkan noindex adalah perintah, apakah hub kosong juga perlu noindex saya biarkan jadi keputusan SEO lead klien. Dua hub yang berisi masih tanpa canonical sendiri, dan og:url di tampilan hub role memantulkan query string apa pun dari request, termasuk tautan utm_. Keduanya kondisi lama, bukan akibat perubahan ini. Soal og:url sudah saya sampaikan, sedangkan untuk dua hub berisi, SEO lead klien mungkin ingin masing-masing diberi canonical yang secara eksplisit menunjuk ke URL-nya sendiri.

Dari luar, URL buatan add_rewrite_rule tampak seperti halaman biasa. Bagi Rank Math, URL itu tidak ada sebagai objek: tidak ada metabox, tidak ada post meta, tidak ada canonical bawaan. Setiap kali halaman publik dibuat lewat aturan rewrite, title, description, dan canonical-nya menjadi urusan kode tema sejak hari pertama.