Lompat ke konten
Server & .htaccess

Aturan Redirect .htaccess WordPress: Bikin 301 yang Benar

Pindahkan URL tanpa kehilangan peringkat: direktif redirect .htaccess WordPress mana yang dipakai, di mana aturannya harus ditaruh, dan cara mengetesnya tanpa terkunci.

Dipublikasikan

Mau memindahkan URL tapi ingin yang lama tetap mempertahankan peringkatnya? Kamu butuh 301 di .htaccess, ditaruh di tempat yang tidak akan dihapus WordPress, ditulis dengan direktif yang tepat, dan diarahkan ke URL persis yang benar-benar dilayani WordPress. Salah satu saja dari ketiganya meleset, hasilnya adalah redirect berantai, loop, atau 500 yang mengunci kamu dari wp-admin.

Keputusannya, aturan penempatannya, lalu tesnya — dalam urutan itu.

Direktif mana: Redirect, RedirectMatch, atau RewriteRule

Ada dua modul Apache yang terlibat dan keduanya tidak bisa saling menggantikan.

Redirect (mod_alias) — satu path yang sudah diketahui ke satu tujuan. Hal paling sederhana yang berhasil:

Redirect 301 /old-page/ https://example.com/new-page/

Ada dua perilaku yang perlu kamu tahu. Direktif ini mencocokkan prefix path, bukan string persis, jadi /old-page/ juga menangkap /old-page/anything/ dan menambahkan /anything/ ke tujuannya. Dan dia membawa query string ikut secara otomatis.

RedirectMatch (mod_alias) — satu regex yang mencakup banyak URL. Beginilah cara memindahkan satu bagian situs sekaligus:

RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

Grup tangkapan (.*) menjadi $1 di tujuannya, jadi /blog/hello-world/ mendarat di /articles/hello-world/. Perhatikan slash di depannya: pola mod_alias mencocokkan path URL secara penuh.

RewriteRule (mod_rewrite) — wajib dipakai kalau redirect-nya bergantung pada sebuah kondisi: hostname, protokol, query string, user agent. Tidak ada satu pun di mod_alias yang bisa menguji hal-hal itu.

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

Pola di dalam .htaccess per-direktori punya slash depan yang dibuang — itulah kenapa di sini polanya ^(.*)$ sementara di contoh mod_alias tadi ^/blog/. Tertukar soal ini adalah alasan tunggal paling umum kenapa aturan hasil salin-tempel diam-diam tidak melakukan apa pun.

Jadikan mod_alias sebagai pilihan default. Ambil RewriteRule hanya kalau kamu butuh kondisi. Lebih sedikit regex, lebih sedikit peluang salah.

Di mana aturannya harus ditaruh

Redirect kustom ditaruh di atas baris # BEGIN WordPress. Bukan di dalamnya.

# --- custom redirects ---
Redirect 301 /old-page/ https://example.com/new-page/
RedirectMatch 301 ^/blog/(.*)$ https://example.com/articles/$1

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Ada dua alasan yang berdiri sendiri:

  1. WordPress menulis ulang bloknya sendiri. Menyimpan Settings → Permalinks memanggil flush_rewrite_rules(), yang membuang semua isi di antara penanda lalu meregenerasinya. Aturan yang kamu taruh di dalamnya akan hilang begitu ada yang menyentuh layar itu — bisa jadi berbulan-bulan kemudian, tanpa ada yang menghubungkan kedua kejadian tersebut.
  2. Urutan menentukan siapa yang menang. Blok WordPress berakhir dengan catch-all yang mengirim setiap request non-file dan non-direktori ke index.php. RewriteRule yang ditaruh setelahnya tidak akan pernah jalan.

Satu komplikasi yang perlu diakui jujur: mod_alias dan mod_rewrite sebenarnya tidak berjalan sesuai urutan di file. Apache memproses mod_alias saat tahap URL translation dan mod_rewrite per-direktori belakangan, saat fixup — jadi satu baris Redirect bisa menang atas RewriteRule yang letaknya di atasnya dalam file. Kalau kamu sampai butuh keduanya pada path yang saling tumpang tindih, pilih satu modul untuk path itu dan tetap di dalamnya. Mendebug perkelahian mod_alias lawan mod_rewrite tidak sepadan dengan satu jam waktumu.

Trailing slash

Redirect kanonik milik WordPress sendiri menambahkan trailing slash ke sebagian besar permalink. Jadi yang ini:

Redirect 301 /old-page/ https://example.com/new-page

menghasilkan dua lompatan — 301 milikmu ke URL tanpa slash, lalu 301 milik WordPress yang menambahkan slash-nya. Rantai redirect tetap meneruskan sinyal peringkat, tapi memboroskan crawl budget dan menambah satu round trip untuk setiap pengunjung.

Muat dulu URL tujuannya di browser, salin URL-nya persis seperti yang akhirnya menetap di address bar, lalu pakai itu. Kalau struktur permalink kamu berakhir dengan .html atau tanpa trailing slash, cocokkan dengan itu. Tidak ada jawaban yang benar secara universal di sini, yang ada hanya “cocokkan dengan apa yang dilayani WordPress”.

Mengetes tanpa mengunci diri sendiri

.htaccess dibaca pada setiap request. Satu kesalahan sintaks mengembalikan 500 untuk seluruh situs termasuk wp-admin, jadi jalur pemulihannya harus sudah ada sebelum kamu membutuhkannya.

Backup dulu file-nya. Lewat SSH:

cp /path/to/wordpress/.htaccess /path/to/wordpress/.htaccess.bak

Kalau situsnya kena 500, timpa balik file yang rusak dengan salinan itu dan situsnya langsung pulih. Biarkan satu sesi SFTP tetap terbuka di jendela lain — login dari nol saat situs sedang mati adalah titik di mana kepanikan dimulai. Perlu dicatat, apachectl configtest tidak mem-parsing .htaccess, jadi dia akan melaporkan konfigurasi yang sehat sementara situsmu sudah mati.

Tes pakai curl, bukan browser. Browser menyimpan respons 301 dengan sangat kuat dan dengan senang hati menunjukkan hasil kemarin:

curl -sI https://example.com/old-page/ | head -n 5

Baca dua hal: baris status harus menyebut 301, dan header Location: harus berisi URL final yang persis. Untuk melihat seluruh rantainya, ikuti sampai habis:

curl -sIL https://example.com/old-page/ | grep -E '^(HTTP|Location)'

Lebih dari satu baris HTTP/1.1 301 berarti kamu membuat rantai. Lebih dari sekitar lima berarti kamu membuat loop, dan curl akan berhenti lalu memberitahumu.

Pakai 302 selama kamu masih ragu. 302 tidak di-cache dengan cara yang sama, jadi kesalahan bisa dibatalkan dalam hitungan detik. Ganti ke 301 setelah curl menunjukkan tujuan yang memang kamu maksud. Ini tidak memakan biaya apa pun dan sudah menyelamatkan lebih banyak situs dibanding kebiasaan lain mana pun di artikel ini.

Kalau sebuah aturan sepertinya sama sekali tidak berefek, pastikan dulu Apache memang membaca file-nya. AllowOverride None pada direktori tersebut membuat Apache mengabaikan .htaccess sepenuhnya, dan nginx mengabaikannya total — cek header Server: dengan curl -I sebelum mendebug regex. Untuk menyusun bloknya dengan sintaks yang tepat sesuai versi Apache dan path instalasi kamu, pakai generator .htaccess WordPress.

Kapan sebaiknya tidak memakai .htaccess sama sekali

Untuk segelintir redirect yang perlu dikelola sendiri oleh editor, plugin redirect yang menyimpan aturannya di database adalah alat yang lebih tepat — dia bertahan melewati migrasi hosting dan tidak butuh SSH. Ada konsekuensi nyatanya: php harus booting dulu untuk melayani redirect-nya, dan itu lebih lambat daripada Apache yang menjawab langsung.

Pakai .htaccess untuk perpindahan struktural dan permanen — ganti domain, ganti nama satu bagian situs, memaksa https atau www. Pakai plugin untuk redirect editorial yang sifatnya satu-satu. Memakai keduanya juga tidak masalah selama kamu tahu lapisan mana yang memiliki URL mana, karena redirect yang didefinisikan di dua tempat adalah bug yang sedang menunggu sore yang buruk.

FAQ

Pertanyaan

Di mana redirect kustom harus ditaruh dalam file .htaccess WordPress?

Di atas baris # BEGIN WordPress, jangan pernah di antara penandanya. WordPress meregenerasi semua isi di antara penanda itu setiap kali ada yang menyimpan layar Permalinks, jadi aturan yang ditaruh di dalamnya akan terhapus tanpa peringatan. Posisi juga menentukan urutan: blok WordPress berakhir dengan catch-all yang mengarahkan semua request yang tidak cocok ke index.php.

Sebaiknya pakai Redirect, RedirectMatch, atau RewriteRule untuk 301?

Pakai Redirect untuk satu path tunggal yang sudah kamu tahu, RedirectMatch kalau satu pola mencakup banyak URL, dan RewriteRule kalau redirect-nya bergantung pada suatu kondisi seperti hostname, protokol, atau query string. Redirect dan RedirectMatch berasal dari mod_alias dan lebih sederhana. RewriteRule berasal dari mod_rewrite dan hanya dia yang bisa menguji kondisi.

Kenapa redirect .htaccess saya menyebabkan redirect loop?

Biasanya karena tujuannya masih cocok dengan aturan yang mengirim pengunjung ke sana, jadi aturannya terpicu terus tanpa henti. Penyebab umum lainnya adalah memaksa https di belakang load balancer atau CDN, di mana server melihat http polos pada setiap request meskipun pengunjungnya sudah berada di https. Uji header X-Forwarded-Proto sebagai gantinya.

Apakah trailing slash berpengaruh pada redirect WordPress?

Ya. Redirect kanonik WordPress menambahkan trailing slash ke sebagian besar permalink, jadi mengarahkan 301 ke URL tanpa slash menghasilkan dua lompatan, bukan satu. Redirect berantai tetap meneruskan sinyal peringkat, tapi memboroskan crawl budget dan memperlambat pengunjung. Cocokkan persis dengan URL final yang dilayani WordPress, termasuk slash-nya.

Bagaimana cara mengetes redirect .htaccess tanpa merusak situs?

Jalankan curl dengan flag head-only ke URL lama dan baca kode status serta header Location sebelum percaya pada browser. Browser menyimpan respons 301 dengan sangat agresif dan akan menampilkan hasil basi. Simpan dulu salinan file yang masih berfungsi, karena satu kesalahan sintaks di .htaccess mengembalikan 500 di setiap halaman termasuk wp-admin.

Kenapa seluruh situs saya mengembalikan error 500 setelah menambahkan redirect?

Satu direktif yang salah bentuk di .htaccess menjatuhkan setiap URL di bawah direktori itu, termasuk admin. Penyebab umumnya adalah pembungkus IfModule yang tidak ditutup, baris RewriteEngine On yang hilang, atau sintaks akses Apache 2.2 dan 2.4 yang tercampur dalam satu file. Hapus blok terakhir yang kamu tambahkan lalu muat ulang untuk memastikan.