Fitur tersembunyi mod_rewrite

119

Tampaknya ada sejumlah mod_rewriteutas yang beredar belakangan ini dengan sedikit kebingungan tentang bagaimana aspek-aspek tertentu bekerja. Sebagai hasilnya saya telah mengumpulkan beberapa catatan tentang fungsionalitas umum, dan mungkin beberapa nuansa yang mengganggu.

Apa fitur / masalah umum lain yang pernah Anda temui mod_rewrite?

Owen
sumber
5
Lihat juga serverfault.com/questions/214512/…
Michael Myers

Jawaban:

203

Di mana harus menempatkan aturan mod_rewrite

mod_rewriteaturan dapat ditempatkan di dalam httpd.conffile, atau di dalam .htaccessfile. jika Anda memiliki akses ke httpd.conf, menempatkan aturan di sini akan menawarkan manfaat kinerja (karena aturan diproses sekali, berbeda dengan setiap kali .htaccessfile dipanggil).

Mencatat permintaan mod_rewrite

Logging dapat diaktifkan dari dalam httpd.conffile (termasuk <Virtual Host>):

# logs can't be enabled from .htaccess
# loglevel > 2 is really spammy!
RewriteLog /path/to/rewrite.log
RewriteLogLevel 2

Kasus penggunaan umum

  1. Untuk menyalurkan semua permintaan ke satu titik:

    RewriteEngine on
    # ignore existing files
    RewriteCond %{REQUEST_FILENAME} !-f   
    # ignore existing directories
    RewriteCond %{REQUEST_FILENAME} !-d   
    # map requests to index.php and append as a query string
    RewriteRule ^(.*)$ index.php?query=$1 
    

    Sejak Apache 2.2.16 Anda juga dapat menggunakan FallbackResource.

  2. Menangani pengalihan 301/302:

    RewriteEngine on
    # 302 Temporary Redirect (302 is the default, but can be specified for clarity)
    RewriteRule ^oldpage\.html$ /newpage.html [R=302]  
    # 301 Permanent Redirect
    RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 
    

    Catatan : pengalihan eksternal secara implisit adalah 302 pengalihan:

    # this rule:
    RewriteRule ^somepage\.html$ http://google.com
    # is equivalent to:
    RewriteRule ^somepage\.html$ http://google.com [R]
    # and:
    RewriteRule ^somepage\.html$ http://google.com [R=302]
    
  3. Memaksa SSL

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. Bendera umum:

    • [R]atau [redirect]- paksa pengalihan (default ke pengalihan sementara 302)
    • [R=301]atau [redirect=301]- memaksa pengalihan permanen 301
    • [L]atau [last]- hentikan proses penulisan ulang (lihat catatan di bawah ini untuk kesalahan umum)
    • [NC]atau [nocase]- tentukan bahwa pencocokan harus peka huruf besar / kecil


    Menggunakan tanda berbentuk panjang seringkali lebih mudah dibaca dan akan membantu orang lain yang datang untuk membaca kode Anda nanti.

    Anda dapat memisahkan beberapa bendera dengan koma:

    RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
    

Jebakan umum

  1. mod_aliasPengalihan gaya pencampuran denganmod_rewrite

    # Bad
    Redirect 302 /somepage.html http://example.com/otherpage.html
    RewriteEngine on
    RewriteRule ^(.*)$ index.php?query=$1
    
    # Good (use mod_rewrite for both)
    RewriteEngine on
    # 302 redirect and stop processing
    RewriteRule ^somepage.html$ /otherpage.html [R=302,L] 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # handle other redirects
    RewriteRule ^(.*)$ index.php?query=$1                 
    

    Catatan : Anda dapat mencampur mod_aliasdengan mod_rewrite, tetapi melibatkan lebih banyak pekerjaan dari sekedar penanganan pengalihan dasar seperti di atas.

  2. Konteks memengaruhi sintaks

    Di dalam .htaccessfile, garis miring di depan tidak digunakan dalam pola RewriteRule:

    # given: GET /directory/file.html
    
    # .htaccess
    # result: /newdirectory/file.html
    RewriteRule ^directory(.*)$ /newdirectory$1
    
    # .htaccess
    # result: no match!
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # httpd.conf
    # result: /newdirectory/file.html
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # Putting a "?" after the slash will allow it to work in both contexts:
    RewriteRule ^/?directory(.*)$ /newdirectory$1
    
  3. [L] bukan yang terakhir! (terkadang)

    The [L]bendera berhenti memproses aturan menulis ulang lebih lanjut untuk itu melewati peraturan yang ditetapkan . Namun, jika URL telah diubah di pass itu dan Anda berada dalam .htaccesskonteks atau <Directory>bagian, maka permintaan Anda yang dimodifikasi akan diteruskan kembali melalui mesin pengurai URL lagi. Dan pada umpan berikutnya, kali ini mungkin cocok dengan aturan yang berbeda. Jika Anda tidak memahami hal ini, seringkali [L]benderanya tampak tidak berpengaruh.

    # processing does not stop here
    RewriteRule ^dirA$ /dirB [L] 
    # /dirC will be the final result
    RewriteRule ^dirB$ /dirC     
    

    Log penulisan ulang kami menunjukkan bahwa aturan dijalankan dua kali dan URL diperbarui dua kali:

    rewrite 'dirA' -> '/dirB'
    internal redirect with /dirB [INTERNAL REDIRECT]
    rewrite 'dirB' -> '/dirC'
    

    Cara terbaik untuk menyiasatinya adalah dengan menggunakan [END]flag ( lihat dokumen Apache ) daripada menggunakan [L]flag, jika Anda benar-benar ingin menghentikan semua pemrosesan aturan lebih lanjut (dan penerusan berikutnya). Namun, [END]flag tersebut hanya tersedia untuk Apache v2.3.9 + , jadi jika Anda memiliki v2.2 atau lebih rendah, Anda tidak dapat lagi menggunakan [L]flag tersebut.

    Untuk versi sebelumnya, Anda harus bergantung pada RewriteCondpernyataan untuk mencegah pencocokan aturan pada penerusan berikutnya dari mesin pengurai URL.

    # Only process the following RewriteRule if on the first pass
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ...
    

    Atau Anda harus memastikan bahwa RewriteRule Anda berada dalam konteks (yaitu httpd.conf) yang tidak akan menyebabkan permintaan Anda diurai ulang.

Owen
sumber
10
Sobat, benar-benar artikel terbaik di internet sekarang di mod rewrite. Aku benci benda itu. Saya seorang bidat lighttpd karena betapa saya benci mod_rewrite.
Kent Fredric
3
Sejauh ini, ini adalah panduan paling berguna yang saya temukan di mod_rewrite. Hanya mencari tahu tentang RewriteLog membantu memperbaiki begitu banyak masalah sehingga yang membutuhkan waktu berhari-hari untuk saya lacak berubah menjadi beberapa menit. (Maksud saya peraturannya sudah tertulis tetapi saya tidak tahu mengapa mereka tidak bekerja)
Joe Chin
Posting lama 1 tahun, tapi salah satu hal yang lebih berguna yang saya temukan di SO - untuk saya.
Erik
3
The [L]bendera berarti aturan adalah terakhir dalam pengolahan saat ini, ini tidak akan berhenti menulis ulang, karena mereka adalah pengalihan internal, sehingga Anda dirBberlaku untuk dirCdalam pengolahan htaccess berikutnya. Sendiri RewriteRule ^(.*)$ index.php?query=$1akan menjadi loop pengalihan internal tak terbatas (dalam praktiknya dihentikan setelah 10 iterasi). -1 karena Anda menyarankan bahwa [L] bukan yang terakhir . Ini tidak menghentikan proses penulisan ulang, tetapi ini yang terakhir .
kbec
3
Saya percaya RewriteCond %{HTTPS} offadalah cara yang disukai untuk memeriksa koneksi HTTPS (dalam contoh Anda memaksa lalu lintas non-ssl ke HTTPS)
Madbreaks
22

jika Anda perlu 'memblokir' pengalihan / penulisan ulang internal agar tidak terjadi di .htaccess, lihat

RewriteCond %{ENV:REDIRECT_STATUS} ^$

kondisi, seperti yang dibahas di sini .

mromaine
sumber
Terima kasih, itu baru saja menyelesaikan masalah saya!
Matius
Terima kasih juga untuk saya, penyelamat!
Benjamin
Ini memang penyelamat hidup! Orang harus lebih sadar akan hal itu. Sebenarnya, saya akan menyarankan ini untuk setiap pertanyaan tentang .*dengan [L]bendera yang saya baca sebelum saya sampai di sini.
Qwerty
Saya telah melihat beberapa modifikasi ini 200, !=200, ^., ^$. Tampaknya variabel disetel ke 200untuk pengalihan, tetapi juga halaman lain (kesalahan dan hal-hal) menyetelnya ke beberapa nilai. Sekarang berarti bahwa Anda juga memeriksa apakah itu is empty, is not empty, is 200atau is not 200, tergantung pada apa yang Anda butuhkan.
Qwerty
18

Kesepakatan dengan RewriteBase:

Anda hampir selalu perlu menyetel RewriteBase. Jika tidak, apache menebak bahwa basis Anda adalah jalur disk fisik ke direktori Anda. Jadi mulailah dengan ini:

RewriteBase /
Sean McMillan
sumber
Ah. Itu benar-benar hanya memperbaiki masalah yang saya alami. Terima kasih untuk itu!
Tom Savage
3
Adakah cara untuk mengatakannya RewriteBase ., atau sesuatu untuk menunjukkan bahwa itu harus menjaga URL tetap sama, hanya mengubah apa yang telah Anda tentukan?
Jay K
Terima kasih, ini adalah info yang sangat berharga. :)
AturSams
2
Anda hanya perlu menyetel RewriteBasejika Anda menggunakan substitusi jalur relatif dalam RewriteRuledirektif. Lebih baik hindari menggunakan jalur relatif.
MrWhite
2
Saya tidak setuju dengan jawaban ini. Dalam tim pengembang kami, kami menghindari RewriteBasesama sekali karena hampir semua pengembang salah paham tentang fungsinya. Seperti yang dikatakan @ w3d, Anda hanya membutuhkannya jika Anda ingin menyimpan karakter dan ingin menerapkan basis yang sama ke semua RewriteRules Anda dalam satu file. Kode Anda kemungkinan akan lebih jelas bagi orang lain jika Anda menghindarinya.
Simon East
13

Jebakan Lainnya:

1- Terkadang ide yang bagus untuk menonaktifkan MultiView

Options -MultiViews

Saya kurang paham tentang semua kemampuan MultiView, tetapi saya tahu bahwa ini mengacaukan aturan mod_rewrite saya saat aktif, karena salah satu propertinya adalah mencoba dan 'menebak' ekstensi ke file yang menurutnya sedang saya cari .

Saya akan menjelaskan: Misalkan Anda memiliki 2 file php di dir web Anda, file1.php dan file2.php dan Anda menambahkan ketentuan dan aturan ini ke .htaccess Anda:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ file1.php/$1 

Anda berasumsi bahwa semua url yang tidak cocok dengan file atau direktori akan diambil oleh file1.php. Mengherankan! Aturan ini tidak diterapkan untuk url http: // myhost / file2 / somepath . Sebaliknya Anda dibawa ke dalam file2.php.

Apa yang terjadi adalah MultiView secara otomatis menebak bahwa url yang sebenarnya Anda inginkan adalah http: //myhost/file2.php/somepath dan dengan senang hati membawa Anda ke sana.

Sekarang, Anda tidak tahu apa yang baru saja terjadi dan Anda pada saat itu mempertanyakan semua yang Anda pikir Anda ketahui tentang mod_rewrite. Anda kemudian mulai bermain-main dengan aturan untuk mencoba memahami logika di balik situasi baru ini, tetapi semakin Anda menguji semakin tidak masuk akal.

Ok, Singkatnya jika Anda ingin mod_rewrite bekerja dengan cara yang mendekati logika, mematikan MultiView adalah langkah yang tepat.

2- Aktifkan FollowSymlinks

Options +FollowSymLinks 

Yang itu, saya tidak begitu tahu detailnya, tapi saya sudah sering melihatnya disebutkan, jadi lakukan saja.

Michael Ekoka
sumber
Terima kasih :) Saya melihat kejutan tak terduga seperti / log / activity berubah menjadi /log.txt/activity .. Terima kasih atas tipnya :) .. komputer yang terlalu buruk tidak pernah membuat hal-hal menyenangkan yang tidak terduga terjadi seperti secara tidak sengaja merayu semua rekan kerja wanita Anda di facebook :)
AturSams
1
+FollowSymLinksdisebutkan dalam dokumentasi sebagai wajib untuk mod_rewritebekerja sama sekali, untuk alasan keamanan yang tidak jelas.
Joey
Dua pernyataan di sini membuat saya khawatir, sangat: 'Saya tidak memahami semua kapabilitas MultiView, tetapi saya tahu bahwa itu mengacaukan aturan mod_rewrite saya saat aktif' dan yang ini 'Yang itu, saya tidak begitu tahu detailnya , tapi aku sudah sering melihatnya disebutkan, jadi lakukan saja. ' Saya berharap orang-orang seperti Anda tidak menulis jawaban di SO tentang hal-hal yang tidak Anda yakini.
TheCarver
1
@PaparazzoKid: Saya pikir Anda salah mengira SO sebagai ensiklopedia. Ini adalah komunitas orang-orang yang berkumpul untuk menyatukan pemahaman tentang teknologi yang mereka kerjakan. Tidak seperti AW White dan Joey sebelum Anda, komentar Anda hampir tidak ada nilainya. MV dan FSL adalah 2 dari banyak opsi Apache. Jawaban saya adalah tentang jebakan ketika bekerja dengan mod_rw secara khusus, modul terpisah, yang bertentangan dengan beberapa opsi dan bekerja dengan yang lain. Saya menjelaskan bagaimana MV memengaruhi mod_rw dan menyebutkan bahwa + FSL adalah rekomendasi yang populer. Joey menegaskan bahwa itu sebenarnya wajib. Apa yang Anda bawa ke meja?
Michael Ekoka
Terima kasih. Saya baru saja menghabiskan bagian terbaik dari satu jam untuk membuat situs lama bekerja dan mencoba men-debug aturan penulisan ulang, hanya untuk menemukan bahwa MultiView menimpa semuanya.
Andrew McCombe
5

Persamaan dapat dilakukan dengan contoh berikut:

RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC]
# %1 is the string that was found above
# %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;"
#RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*)
# <> is used as an string separator/indicator, can be replaced by any other character
RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC]
RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L]

Penyeimbangan Beban Dinamis:

Jika Anda menggunakan mod_proxy untuk menyeimbangkan sistem Anda, dimungkinkan untuk menambahkan rentang dinamis server pekerja.

RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC]
RewriteRule (.*) https://worker%1.internal.com/$1 [P,L]
DrDol
sumber
4

Pemahaman yang lebih baik tentang bendera [L] sudah beres. Bendera [L] adalah yang terakhir, Anda hanya perlu memahami apa yang akan menyebabkan permintaan Anda dirutekan lagi melalui mesin parsing URL. Dari dokumen ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (penekanan milik saya):

Bendera [L] menyebabkan mod_rewrite berhenti memproses set aturan. Dalam kebanyakan konteks, ini berarti jika aturan cocok, tidak ada aturan lebih lanjut yang akan diproses. Ini sesuai dengan perintah terakhir di Perl, atau perintah break di C. Gunakan tanda ini untuk menunjukkan bahwa aturan saat ini harus diterapkan segera tanpa mempertimbangkan aturan lebih lanjut.

Jika Anda menggunakan RewriteRule baik di file .htaccess atau di <Directory>bagian , penting untuk memiliki pemahaman tentang bagaimana aturan diproses. Bentuk yang disederhanakan adalah bahwa setelah aturan diproses, permintaan yang ditulis ulang akan dikembalikan ke mesin pengurai URL untuk melakukan apa yang mungkin dilakukan dengannya. Ada kemungkinan bahwa saat permintaan penulisan ulang ditangani, file atau<Directory> bagian.htaccessdapat ditemukan lagi, dan dengan demikian kumpulan aturan dapat dijalankan lagi dari awal. Biasanya hal ini akan terjadi jika salah satu aturan menyebabkan pengalihan - baik internal maupun eksternal - yang menyebabkan proses permintaan dimulai kembali.

Jadi [L] Bendera tidak berhenti memproses aturan menulis ulang lebih lanjut untuk yang lulus melalui set aturan. Namun, jika aturan Anda yang ditandai dengan [L] mengubah permintaan tersebut, dan Anda berada dalam konteks .htaccess atau <Directory>bagian tersebut, maka permintaan yang telah diubah tersebut akan diteruskan kembali melalui mesin pengurai URL lagi. Dan pada umpan berikutnya, kali ini mungkin cocok dengan aturan yang berbeda. Jika Anda tidak mengerti apa yang terjadi, sepertinya aturan penulisan ulang pertama Anda dengan bendera [L] tidak berpengaruh.

Cara terbaik untuk menyiasatinya adalah dengan menggunakan flag [AKH] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) alih-alih flag [L], jika Anda benar-benar ingin berhenti semua pemrosesan aturan lebih lanjut (dan penguraian ulang berikutnya). Akan tetapi, flag [END] hanya tersedia untuk Apache v2.3.9 +, jadi jika Anda memiliki v2.2 atau lebih rendah, Anda tidak bisa lagi menggunakan flag [L]. Dalam kasus ini, Anda harus mengandalkan pernyataan RewriteCond untuk mencegah pencocokan aturan pada proses berikutnya dari mesin parsing URL. Atau Anda harus memastikan bahwa RewriteRule Anda berada dalam konteks (mis. Httpd.conf) yang tidak akan menyebabkan permintaan Anda diurai ulang.

JaredC
sumber
3

Fitur hebat lainnya adalah rewrite-map-expansions. Mereka sangat berguna jika Anda memiliki banyak host / penulisan ulang untuk ditangani:

Mereka seperti pengganti nilai kunci:

RewriteMap examplemap txt:/path/to/file/map.txt

Kemudian Anda dapat menggunakan pemetaan dalam aturan Anda seperti:

RewriteRule ^/ex/(.*) ${examplemap:$1}

Informasi lebih lanjut tentang topik ini dapat ditemukan di sini:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

MENJADI
sumber
Abaikan fitur ini jika Anda menggunakan .htaccesspenulisan ulang berbasis. Itu tidak bekerja dalam konteks ini.
TerryE
2
Direktif RewriteMap harus digunakan dalam konteks server (httpd.conf), tetapi setelah ditentukan di sana, Anda dapat menggunakan peta melalui RewriteRule dalam file .htaccess.
JaredC
2

mod_rewrite dapat memodifikasi aspek penanganan permintaan tanpa mengubah URL, misalnya mengatur variabel lingkungan, mengatur cookie, dll. Ini sangat berguna.

Tetapkan variabel lingkungan secara bersyarat:

RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC]
RewriteRule .* - [E=MY_ENV_VAR:%b]

Kembali respon 503: RewriteRule's [R]bendera dapat mengambil nilai non-3xx dan kembali respon non-mengarahkan, misalnya untuk dikelola downtime / pemeliharaan:

RewriteRule .* - [R=503,L]

akan mengembalikan respons 503 (bukan pengalihan per se).

Selain itu, mod_rewrite dapat bertindak seperti antarmuka bertenaga super untuk mod_proxy, jadi Anda dapat melakukan ini alih-alih menulis ProxyPassarahan:

RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]

Opini: Menggunakan RewriteRules dan RewriteConds untuk merutekan permintaan ke aplikasi atau penyeimbang beban yang berbeda berdasarkan hampir semua aspek permintaan yang mungkin sangat kuat. Mengontrol permintaan dalam perjalanannya ke backend, dan dapat mengubah tanggapan saat keluar, menjadikan mod_rewrite tempat yang ideal untuk memusatkan semua konfigurasi yang terkait dengan perutean.

Luangkan waktu untuk mempelajarinya, itu sangat berharga! :)

minggu
sumber