Semalaman penuh aku dipusingkan karena selalu gagal dalam membuat rule untuk rewrite URL. Rule yang aku buat selalu gagal, padahal secara logic itu sudah bener.
Sebenarnya keinginan aku simpel. Internal redirect semua request selain ke directory /js, /img, /css dan /pub ke index.php?path=%URL%. Jika URL tidak di akhiri dengan / maka external redirect dengan URL yang sama tapi di akhiri dengan /. Ini karena aku ingin mengemulasikan bahwa request URL di anggap sebagai request ke folder kecuali jika request mempunyai pattern .*[\.].* (file like dan request ini tidak diperbolehkan).
Ketika pertama kali membuat rule dengan rewrite URL normal dari /home ke model /index.php?path=home semua berjalan baik termasuk rule allow directory khusus semua berjalan baik. Masalah timbul ketika mencoba membuat rule untuk emulasi request directory. Request untuk /home gagal diexternal redirect ke /home/ tetapi diexternal redirect ke /index.php/?path=index.php/&path=home
Penasaran kenapa bisa terjadi seperti ini, aku coba untuk melihat access.log milik Apache. Sungguh hal yang tak diduga, Apache menerima 3 request yang aneh:
127.0.0.1 - - [18/Aug/2007:01:59:29 +0700] "GET /FR/home HTTP/1.1" 302 370
127.0.0.1 - - [18/Aug/2007:01:59:29 +0700] "GET /FR/home/ HTTP/1.1" 302 390
127.0.0.1 - - [18/Aug/2007:01:59:29 +0700] "GET /FR/index.php/?path=index.php/&path=home HTTP/1.1" 200 155
Request pertama memang seperti yang aku inginkan, karena memang begitulan jika terjadi request pada directory. Tapi request yang kedua terjadi keanehan karena seharusnya ModRewrite mendeteksi URL model kedua adalah valid dah langsung di internal redirect tapi kok malah dilaporkan temporary moved.
Akhirnya aku harus tenggelam pada manual ModRewrite Apache (menyedihkan memang, karena aku tidak punya manual untuk ModRewrite penuh — menang ada gak ya? dunno!) dan voila… aku temukan bahwa ModRewrite punya logger… (wkwkwkw kenapa tidak mulai pertama aku temukan ini)
Dengan bantuan logger ini aku coba menyelam lebih dalam bagaimana ModRewrite menangani rule-rule yang aku buat dan aku temukan bahwa rule yang buat di handle tidak seperti bagaimana logic yang aku inginkan.
Ini rule yang aku definikan:
RewriteCond $1 =js [NC,OR]
RewriteCond $1 =img [NC,OR]
RewriteCond $1 =css [NC,OR]
RewriteCond $1 =pub [NC]
RewriteRule ^([^/]*) - [L]
RewriteRule ^.*[^/]$ ${REQUEST_URI}/ [L,R]
#RewriteRule ^(.*[^/])$ index.php?path=$1 [L,QSA]
RewriteRule ^(.*/)$ index.php?path=$1 [L,QSA]
Rule ke 3 dicomment karena seharusnya tidak ada lagi URL tanpa forwardslash karena jika ada akan diexternal redirectkan oleh rule ke 2.
Untuk lebih masuk kebagaimana yang aku inginkan terjadi, aku coba uraikan logic yang aku inginkan pada request /home
Pertama /home akan dibandingkan dengan rule nomor 1 dan pass it, rule ke 2 unpass karena /home tidak mempunyai postfix / dan harus diexternal redirect. Request hasil dari external redirect adalah /home/ dan ini akan pass rule 1 dan 2 tapi tidak untuk rule 4 (rule 3 dicomment out) sehingga hasilnya seperti yang aku inginkan.
Tapi ternyata tidak, mengapa? karena (setelah berjam-jam pusing dan berjam-jam pula melototin hasil log ModRewrite — ampun dah gitu aja butuh berjam-jam) ModRewrite tidak berjalan persis seperti logic yang aku inginkan.
ModRewrite ternyata menjalankan rule tersebut sekali lagi (tepatnya — mungkin akan — berkali-kali) setelah external redirect, sehingga ketika round pertama URL sukses diexternal redirect ke /home/ dan direwrite ke /index.php?path=home. Tapi pada round kedua URL ini unpass pada rule ke 2 karena memang pada query string tidak terdapat forwardslash sehingga diexternal redirect lagi dengan bantuk /index.php/?path=index.php/&path=home. What a wierd… (or its me that too wierd — wkwkwkwk).
Dan pada request ketiga baru URL benar karena rule 1, 2 semuanya pass tapi rule 4 membarikan hasil yang aneh (tapi benar secara ModRewrite) yaitu /index.php/?path=index.php/&path=home. Path yang kedua adalah hasil dari rewrite pertama ketika request diexternal redirect untuk pertama kali.
What a f**k… Woke, akhirnya (penjang sebenarnya karena setelah berjam-jam aku coba untuk mencari cara yang efektif dan baik — menurutku) aku ubah rule menjadi:
RewriteEngine on
# set to base rule
RewriteBase /FR/
# pass if query was correct ie. /index.php?path=home
RewriteCond %{QUERY_STRING} path= [NC]
RewriteRule ^index.php$ - [L]
# request was / or /index.php
RewriteCond %{QUERY_STRING} !path= [NC]
RewriteCond $1 ^index.php$ [OR]
RewriteCond $1 ^$
RewriteRule (.*) index.php?path= [L,QSA]
# allow direct request pada url yang diawali /js, /img, /css dan /pub
RewriteCond $1 js [NC,OR]
RewriteCond $1 img [NC,OR]
RewriteCond $1 css [NC,OR]
RewriteCond $1 pub [NC]
RewriteRule ^([^/]*) - [L]
# report object is missing if request is a file
RewriteRule ^(.*\..*)$ - [L,R=404]
# redirect if request is not a valid path model
RewriteCond %{REQUEST_URI} ^.*[^/]$
RewriteRule ^(.+)$ %{REQUEST_URI}/ [L,R,NS]
# rewrite to corrent one. ie. /home/ to /index.php?path=home
RewriteRule (.*) index.php?path=$1 [L,QSA]
Rule ini membarikan apa yang aku inginkan, rule ini akan memperbolehkan request dalam bentuk apapun selama request diawali dengan /js, /img, /css dan /pub (rule 3), melarang request pattern file selain /index.php (rule 1, 2, 3, 4), mengexternal redirectkan request path non postfix forwardslashed seperti /home ke /home (emulasi request folder, rule 5) dan merewrite semua request valid ke bentuk /index.php?path={REQUEST_PATH} (rule 6).
Tapi sayang rule ini masih belum bisa menangani bahwa request /index.php hanya boleh via rewrite URL (rule 2 dan 6), mungkin lain waktu aku akan begadang untuk mencari penyelesainnya.
Oke, cukup disini petualangan dari ModRewrite. Sudah semalaman begadang gara-gara INTERNAL REDIRECT — begitu log ModRewrite menyebutkan ketika rule di jalankan untuk round ke 2 karena URL direwrite ulang — sekarang udah pagi, jam 5.14 AM — aku mulai membuat rule jam 22 PM -an dengan menelantarkan perkerjaan wajib aku (sedih… banget).
