Menggunakan aturan penulisan ulang Apache di .htaccess untuk menghapus .html menyebabkan kesalahan 500
Saya telah menulis situs web kecil (4 halaman, hanya HTML) dan saya ingin menghapus ekstensi .html dari URL dengan meletakkan beberapa aturan penulisan ulang di file .htaccess saya, saya telah mencari-cari di Google dan menemukan beberapa cuplikan yang mirip dengan ini:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
</IfModule>
Kedua URL berikut menyajikan konten yang sama (yang saya harapkan)
https://example.io/contact
https://example.io/contact.html
Namun yang berikut ini memberikan kesalahan 500:
https://example.io/contact/
Direktori ini tidak ada dan jika saya menghapus kode penulisan ulang yang disebutkan di atas, itu akan menjadi 404, seperti yang saya harapkan. Mengapa kode di atas menyebabkan kesalahan 500?
Yang lebih menarik adalah bahwa ini akan 500:
https://example.io/contact/blah
Tapi ini akan 404:
https://example.io/contact123/blah
Baik contact / atau contact123 / ada sebagai direktori tetapi contact.html ada dan contact123.html tidak ada.
Bantuan atau penjelasan apa pun akan dihargai.
Edit:
MrWhite telah memberikan jawaban yang benar tetapi bagi siapa saja yang melihat di masa depan log kesalahan Apache terlihat seperti ini:
[Thu Oct 24 20:49:47.722210 2019] [core:error] [pid 13001:tid 139915446667008] [client 1.2.3.4:39006] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
Saya telah memeriksa log dan tidak yakin mengapa itu terjadi tetapi lupa untuk memasukkan ini ke dalam pertanyaan.
Jawaban
tl; dr Permintaan untuk /contact/
(atau /contact/blah
) menghasilkan loop penulisan ulang (500 respons Internal Server Error) karena REQUEST_FILENAME
berisi jalur sistem file yang dipetakan; bukan jalur URL yang Anda harapkan.
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME}\.html -f RewriteRule ^(.*)$ $1.html
"Masalah" adalah penggunaan REQUEST_FILENAME
dalam kondisi ke-2. The REQUEST_FILENAME
variabel server berisi path filesystem mutlak setelah URL telah dipetakan ke filesystem. Ini belum tentu sama dengan jalur URL - tetapi ketentuan ini mengasumsikan bahwa itu benar. Ketika jalur URL berisi seluruh segmen jalur yang tidak dipetakan ke sistem file (seperti dalam /contact/blah
atau /contact123/blah
) maka pada REQUEST_FILENAME
dasarnya "dikurangi" ke segmen jalur terakhir yang memetakan ke direktori, ditambah "nama file" (yaitu .../contact
dan .../contact123
masing-masing - root dokumen, yaitu /
, direktori yang cocok terakhir dalam contoh ini).
Permintaan /contact
Ketika Anda meminta /contact
maka URL-jalan adalah /contact
dan REQUEST_FILENAME
adalah /path/to/document-root/contact
- sehingga REQUEST_FILENAME
peta langsung ke URL-jalan. Kondisi pengujian /path/to/document-root/contact.html
berhasil dan permintaan ditulis ulang menjadi contact.html
. Semua baik.
Minta /contact/
atau/contact/blah
Namun, ketika Anda meminta /contact/
maka jalur URL adalah /contact/
, tetapi REQUEST_FILENAME
lagi /path/to/document-root/contact
(tanpa akhiran garis miring). Kondisi pengujian kembali berhasil (seperti di atas), tetapi permintaan ditulis ulang ke contact/.html
(karena .html
ditambahkan ke jalur URL yang diambil , yaitu $1.html
). Memproses loop, REQUEST_FILENAME
mengevaluasi ke yang sama seperti sebelumnya (kondisi kembali berhasil) dan permintaan ditulis ulang untuk kedua kalinya contact/.html.html
. Dll, dll, menghasilkan loop penulisan ulang yang akhirnya mencapai batas internal (default 10) saat "rusak" dan server merespons dengan 500 Internal Server Error.
Permintaan /contact123/blah
/contact123/blah
, di sisi lain, menghasilkan 404 karena REQUEST_FILENAME
variabel server menjadi /path/to/document-root/contact123
dan /path/to/document-root/contact123.html
tidak ada, jadi tidak ada penulisan ulang sejak awal.
"Larutan"
Untuk "memperbaiki" perilaku ini, Anda harus menggunakan REQUEST_URI
variabel server. Ini berisi jalur URL root-relative. Tambahkan ini ke DOCUMENT_ROOT
variabel server untuk membuat nama file yang akan diuji.
Sebagai contoh:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule (.*) $1.html [L]
Sekarang, kondisi pengujian sedang menguji jalur sistem file yang sama dengan tempat permintaan akan ditulis ulang (jika berhasil).
Permintaan untuk /contact/
, /contact/blah
atau /contact123/blah
semuanya sekarang menghasilkan 404 seperti yang diharapkan.
Perhatikan bahwa tidak perlu membuat garis miring terbalik dari titik literal di RewriteCond
TestString karena ini bukan regex.
Poin kecil ... jangkar ^
dan tidak diperlukan karena regex secara default rakus (meskipun beberapa pengguna tampaknya masih menyukainya agar mudah dibaca ?). Anda juga harus menyertakan flag ( ) di . Meskipun ini tidak diperlukan jika ini adalah satu-satunya (atau terakhir) aturan dalam file, jika Anda harus menambahkan lebih banyak aturan nanti maka itu mungkin (dan harus ingat untuk mengubah aturan yang ada dengan cara ini rentan terhadap kesalahan).$
^(.*)$
L
last
RewriteRule
.htaccess