Menggunakan aturan penulisan ulang Apache di .htaccess untuk menghapus .html menyebabkan kesalahan 500

Oct 25 2019

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

3 MrWhite Oct 25 2019 at 07:40

tl; dr Permintaan untuk /contact/(atau /contact/blah) menghasilkan loop penulisan ulang (500 respons Internal Server Error) karena REQUEST_FILENAMEberisi 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_FILENAMEdalam kondisi ke-2. The REQUEST_FILENAMEvariabel 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/blahatau /contact123/blah) maka pada REQUEST_FILENAMEdasarnya "dikurangi" ke segmen jalur terakhir yang memetakan ke direktori, ditambah "nama file" (yaitu .../contactdan .../contact123masing-masing - root dokumen, yaitu /, direktori yang cocok terakhir dalam contoh ini).

Permintaan /contact

Ketika Anda meminta /contactmaka URL-jalan adalah /contactdan REQUEST_FILENAMEadalah /path/to/document-root/contact- sehingga REQUEST_FILENAMEpeta langsung ke URL-jalan. Kondisi pengujian /path/to/document-root/contact.htmlberhasil 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_FILENAMElagi /path/to/document-root/contact(tanpa akhiran garis miring). Kondisi pengujian kembali berhasil (seperti di atas), tetapi permintaan ditulis ulang ke contact/.html(karena .htmlditambahkan ke jalur URL yang diambil , yaitu $1.html). Memproses loop, REQUEST_FILENAMEmengevaluasi 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_FILENAMEvariabel server menjadi /path/to/document-root/contact123dan /path/to/document-root/contact123.htmltidak ada, jadi tidak ada penulisan ulang sejak awal.

"Larutan"

Untuk "memperbaiki" perilaku ini, Anda harus menggunakan REQUEST_URIvariabel server. Ini berisi jalur URL root-relative. Tambahkan ini ke DOCUMENT_ROOTvariabel 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/blahatau /contact123/blahsemuanya 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).$^(.*)$LlastRewriteRule.htaccess