Mod_rewrite'ın gizli özellikleri


119

mod_rewriteSon zamanlarda, belirli yönlerinin nasıl çalıştığına dair biraz kafa karışıklığı ile dolaşan makul sayıda konu var gibi görünüyor . Sonuç olarak, ortak işlevler üzerine birkaç not ve belki de birkaç can sıkıcı nüans derledim.

Başka hangi özellikleri / yaygın sorunları kullanırken karşılaştınız mod_rewrite?


Yanıtlar:


203

Mod_rewrite kuralları nereye yerleştirilir

mod_rewritekurallar httpd.confdosyanın içine veya dosyanın içine yerleştirilebilir .htaccess. erişiminiz varsa httpd.conf, buraya kural koymak bir performans avantajı sağlayacaktır (çünkü kurallar .htaccessdosya her çağrıldığında değil, bir kez işlenir ).

Mod_rewrite isteklerini günlüğe kaydetme

Günlük kaydı httpd.confdosya içinden etkinleştirilebilir (dahil <Virtual Host>):

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

Yaygın kullanım durumları

  1. Tüm istekleri tek bir noktaya yönlendirmek için:

    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 
    

    Apache 2.2.16'dan beri FallbackResource.

  2. 301/302 yönlendirmelerini işleme:

    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] 
    

    Not : harici yönlendirmeler örtük olarak 302 yönlendirmelerdir:

    # 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. SSL'yi zorlamak

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

    • [R]veya [redirect]- bir yeniden yönlendirmeyi zorla (varsayılan olarak 302 geçici yeniden yönlendirmedir)
    • [R=301]veya [redirect=301]- 301 kalıcı yeniden yönlendirmeye zorlamak
    • [L]veya [last]- yeniden yazma sürecini durdurun (genel tuzaklar için aşağıdaki nota bakın)
    • [NC]veya [nocase]- eşleşmenin büyük / küçük harfe duyarlı olmaması gerektiğini belirtin


    Uzun bayrak biçimlerini kullanmak genellikle daha okunabilirdir ve daha sonra kodunuzu okumaya gelenlere yardımcı olur.

    Birden çok bayrağı virgülle ayırabilirsiniz:

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

Ortak tuzaklar

  1. mod_aliasStil yönlendirmelerinin karıştırılmasımod_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                 
    

    Not : mod_aliasile karıştırabilirsiniz mod_rewrite, ancak yukarıdaki gibi temel yönlendirmeleri işlemekten daha fazlasını gerektirir.

  2. Bağlam sözdizimini etkiler

    İçinde .htaccessdosyalar, önde gelen çizgi RewriteRule desende kullanılmaz:

    # 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] son ​​değil! (ara sıra)

    [L]Bayrak başka kuralları yeniden işlemeyi durdurur kural kümesi içinden o geçitten için . Bununla birlikte, URL bu geçişte değiştirilmişse ve .htaccessiçerik veya <Directory>bölümdeyseniz, değiştirilen isteğiniz URL ayrıştırma motorundan tekrar geri gönderilecektir. Ve bir sonraki geçişte, bu sefer farklı bir kuralla eşleşebilir. Bunu anlamazsan, genellikle senin gibi görünür[L] bayrağınızın hiçbir etkisi yokmuş .

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

    Yeniden yazma günlüğümüz, kuralların iki kez çalıştırıldığını ve URL'nin iki kez güncellendiğini gösterir:

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

    Bunu aşmanın en iyi yolu , kuralların tüm işlemlerini (ve sonraki geçişleri) gerçekten durdurmak istiyorsanız , bayrak yerine [END]bayrak kullanmaktır ( bkz. Apache belgeleri[L] ). Ancak [END]bayrak yalnızca Apache v2.3.9 + için kullanılabilir , bu nedenle v2.2 veya daha düşük bir sürümünüz varsa[L] .

    Önceki sürümler için, RewriteCondURL ayrıştırma motorunun sonraki geçişlerinde kuralların eşleşmesini önlemek için ifadelere güvenmeniz gerekir .

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

    Veya RewriteRule'larınızın httpd.conf, talebinizin yeniden ayrıştırılmasına neden olmayacak bir bağlamda (yani ) olduğundan emin olmalısınız .


10
Ahbap, tamamen internetteki en iyi makale şimdi mod yeniden yazma ile ilgili. Nefret ediyorum o şeyden. Im lighttpd sapkın çünkü ben mod_rewrite'ın ne kadar nefret arasında.
Kent Fredric

3
Bu şimdiye kadar mod_rewrite'da bulduğum en kullanışlı kılavuz oldu. Sadece RewriteLog hakkında bilgi edinmek o kadar çok sorunun çözülmesine yardımcı oldu ki, günlerimi bulmam birkaç dakikaya dönüştü. (Demek istediğim, kurallar yazılmıştı ama neden işe yaramadıklarını anlayamadım)
Joe Chin

1 yıllık bir gönderi, ancak SO'da bulduğum daha yararlı şeylerden biri - benim için.
Erik

3
[L]Bayrak bir kuraldır anlamı son onlar iç yönlendirme çünkü senin böylece cari işlemede, bu, yeniden durmayacak dirBiçin geçerli dirCbir sonraki htaccess işlemede. Tek RewriteRule ^(.*)$ index.php?query=$1başına, sonsuz bir dahili yeniden yönlendirme döngüsü olacaktır (pratikte 10 yinelemeden sonra sonlandırılır). -1 çünkü [L] 'nin son olmadığını öneriyorsunuz . Yeniden yazma sürecini sonlandırmıyor ama sonuncu .
kbec

3
RewriteCond %{HTTPS} offHTTPS bağlantısını kontrol etmenin tercih edilen yolu olduğuna inanıyorum (SSL olmayan trafiği HTTPS'ye zorlama örneğinizde)
Madbreaks

22

.htaccess'te dahili yeniden yönlendirmelerin / yeniden yazmaların olmasını 'engellemeniz' gerekiyorsa, şu adrese bir göz atın:

RewriteCond %{ENV:REDIRECT_STATUS} ^$

Durum, burada tartışıldığı gibi .


Teşekkürler, bu sadece sorunum çözüldü!
Matthew

Benim için de teşekkürler, hayat kurtarıcı!
Benjamin

Bu gerçekten bir hayat kurtarıcı! İnsanlar bunun daha çok farkında olmalı. Hakkında Aslında, her soruya bu önermek için gidiyorum .*ile [L]bayrak okuduğum Ben buraya gelmeden önce.
Qwerty

Bunun için birkaç değişiklik gördük 200, !=200, ^., ^$. Görünüşe göre değişken 200bir yönlendirme için ayarlanır , ancak diğer sayfalar da (hata ve şeyler) onu bir değere ayarlar. Şimdi araç ya kontrol etmenizi bunun ise is empty, is not empty, is 200veya is not 200, ne ihtiyaca bağlı.
Qwerty

18

RewriteBase ile anlaşma:

Hemen hemen her zaman RewriteBase'i ayarlamanız gerekir. Bunu yapmazsanız, apache, temelinizin dizininize giden fiziksel disk yolu olduğunu tahmin eder. Öyleyse bununla başlayın:

RewriteBase /

Ah. Bu, yaşadığım sorunu tamamen çözdü. Bunun için teşekkürler!
Tom Savage

3
Söylemenin herhangi bir yolu RewriteBase .veya URL'yi aynı tutması gerektiğini belirten bir şey, sadece belirttiğiniz şeyi değiştirerek?
Jay K

Teşekkürler, bu paha biçilemez bir bilgiydi. :)
AturSams

2
RewriteBaseYönergede göreceli yol ikamesi kullanıyorsanız ayarlamanız yeterlidir RewriteRule. Göreli yolları kullanmaktan kaçınmak daha iyidir.
MrWhite

2
Bu cevaba katılmıyorum. Geliştirme ekibimizde RewriteBaseneredeyse tüm geliştiriciler ne yaptığını yanlış anladığından tamamen kaçınıyoruz . @ W3d'nin dediği gibi, sadece karakterleri kaydetmek ve aynı temeli tüm RewriteRules'inize tek bir dosyada uygulamak istiyorsanız buna ihtiyacınız var. Bundan kaçınırsanız, kodunuz muhtemelen başkaları için daha net olacaktır.
Simon East

13

Diğer Tuzaklar:

1- Bazen MultiViews'ı devre dışı bırakmak iyi bir fikirdir

Options -MultiViews

Tüm MultiViews yeteneklerine hakim değilim, ancak aktifken mod_rewrite kurallarımı karıştırdığını biliyorum, çünkü özelliklerinden biri aradığımı düşündüğü bir dosyanın uzantısını denemek ve 'tahmin etmek'. .

Açıklayacağım: Web dizininizde dosya1.php ve file2.php'de 2 php dosyanız olduğunu ve bu koşulları ve kuralı .htaccess'e eklediğinizi varsayalım:

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

Bir dosya veya dizinle eşleşmeyen tüm url'lerin file1.php tarafından alınacağını varsayarsınız. Sürpriz! Bu kurala http: // myhost / file2 / somepath url'si için uyulmuyor . Bunun yerine file2.php içine alınırsınız.

Olan şey şu ki, MultiViews otomatik olarak istediğiniz url'nin http: //myhost/file2.php/somepath olduğunu tahmin etti sizi oraya memnuniyetle götürdü.

Şimdi, ne olduğu hakkında hiçbir fikriniz yok ve o noktada mod_rewrite hakkında bildiğinizi sandığınız her şeyi sorguluyorsunuz. Daha sonra bu yeni durumun arkasındaki mantığı anlamaya çalışmak için kurallarla oynamaya başlarsınız, ancak ne kadar çok test ederseniz, o kadar mantıklı olmaz.

Tamam, kısaca mod_rewrite'ın mantığa yakın bir şekilde çalışmasını istiyorsanız, MultiViews'ı kapatmak doğru yönde atılmış bir adımdır.

2- FollowSymlinks'i etkinleştirin

Options +FollowSymLinks 

Bunun ayrıntılarını gerçekten bilmiyorum ama defalarca bahsettiğini gördüm, o yüzden sadece yap.


Teşekkürler :) / log / activity gibi beklenmedik sürprizlerin /log.txt/activity'ye dönüştüğünü fark ettim .. Bahşiş için teşekkürler :) .. çok kötü bilgisayarlar, facebook'taki tüm kadın meslektaşlarınızı yanlışlıkla baştan çıkarmak gibi beklenmedik şeyler asla eğlenmez :)
AturSams

1
+FollowSymLinksbelgelerde mod_rewritebelirsiz güvenlik nedenlerinden ötürü çalışmak için zorunlu olarak bahsedilmektedir .
Joey

Buradaki iki ifade beni son derece endişelendiriyor: 'Tüm MultiViews yetenekleri konusunda bilgili değilim, ancak aktifken mod_rewrite kurallarımı bozduğunu biliyorum' ve bu 'Bu, gerçekten ayrıntılarını bilmiyorum , ama ondan defalarca bahsedildiğini gördüm, o yüzden sadece yap. ' Keşke senin gibi insanlar emin olmadığınız şeyler hakkında SO'ya cevaplar yazmasın.
TheCarver

1
@PaparazzoKid: Sanırım SO'yu bir ansiklopedi ile karıştırıyorsunuz. Birlikte çalıştıkları teknolojiyi anlamak için bir araya gelen insanlardan oluşan bir topluluktur. Senden önceki AW White ve Joey'in aksine, yorumunuz neredeyse geçersizdir. MV ve FSL, Apache'nin birçok seçeneğinden ikisidir. Cevabım, özellikle mod_rw ile çalışırken, bazı seçeneklerle çakışan ve diğerleriyle çalışan ayrı bir modülle ilgili tuzaklar hakkındadır. MV'nin mod_rw'yi nasıl etkilediğini anlattım ve + FSL'nin popüler bir öneri olduğunu söyledim. Joey bunun zorunlu olduğunu doğruladı. Masaya ne getiriyorsun?
Michael Ekoka

Teşekkürler. Bir saatin en iyi kısmını eski bir siteyi çalıştırarak ve yeniden yazma kurallarında hata ayıklamaya çalışarak harcadım, ancak MultiViews her şeyi geçersiz kıldığını bulmak için.
Andrew McCombe

5

Denklem aşağıdaki örnekle yapılabilir:

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]

Dinamik Yük Dengeleme:

Sisteminizi dengelemek için mod_proxy kullanırsanız, dinamik bir çalışan sunucu aralığı eklemek mümkündür.

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

4

[L] bayrağının daha iyi anlaşılması gerekir. Son olarak [L] bayrağı , isteğinizin tekrar URL ayrıştırma motoruna yönlendirilmesine neyin sebep olacağını anlamanız gerekir. Dokümanlardan ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (vurgu benim):

[L] bayrağı mod_rewrite'ın kural kümesini işlemeyi durdurmasına neden olur. Çoğu bağlamda, bu, kural eşleşirse başka hiçbir kuralın işlenmeyeceği anlamına gelir. Bu, Perl'deki son komuta veya C'deki break komutuna karşılık gelir. Mevcut kuralın başka kurallar dikkate alınmadan hemen uygulanması gerektiğini belirtmek için bu bayrağı kullanın.

RewriteRule'u .htaccess dosyalarında veya <Directory>bölümlerde kullanıyorsanız , kuralların nasıl işlendiğini biraz anlamanız önemlidir. Bunun basitleştirilmiş şekli, kurallar işlendikten sonra , yeniden yazılan isteğin, yapabileceği şeyi yapması için URL ayrıştırma motoruna geri teslim edilmesidir. Yeniden yazılan istek işlendiğinde, .htaccess dosyası veya<Directory> bölümüyle tekrar karşılaşılması ve bu nedenle kural kümesinin baştan tekrar çalıştırılması mümkündür. Genellikle bu, kurallardan biri, istek sürecinin yeniden başlamasına neden olan dahili veya harici bir yönlendirmeye neden olursa gerçekleşir.

[L] bayrak Yani yapar durdurmak için başka kuralları yeniden işleme o geçitten kural kümesi içinden. Ancak, [L] ile işaretlenmiş kuralınız isteği değiştirdiyse ve .htaccess bağlamında veya <Directory>bölümdeyseniz, değiştirilmiş isteğiniz URL ayrıştırma motorundan tekrar geri gönderilecektir. Ve bir sonraki geçişte, bu sefer farklı bir kuralla eşleşebilir. Ne olduğunu anlamazsanız, [L] işaretli ilk yeniden yazma kuralınızın hiçbir etkisi yok gibi görünüyor.

Bunu aşmanın en iyi yolu , gerçekten durdurmak istiyorsanız, [L] bayrağı yerine [END] işaretini ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) kullanmaktır. kuralların daha fazla işlenmesi (ve müteakip yeniden ayrıştırma). Ancak, [END] bayrağı yalnızca Apache v2.3.9 + için kullanılabilir, bu nedenle v2.2 veya daha düşük bir sürümünüz varsa, yalnızca [L] bayrağına takılı kalırsınız. Bu durumda, URL ayrıştırma motorunun sonraki geçişlerinde kuralların eşleşmesini önlemek için RewriteCond ifadelerine güvenmeniz gerekir. Veya RewriteRule'larınızın, talebinizin yeniden ayrıştırılmasına neden olmayacak bir bağlamda (yani httpd.conf) olduğundan emin olmalısınız.


3

Diğer bir harika özellik, yeniden yazma-harita-genişletmeleridir. Bunların üstesinden gelinmesi gereken çok sayıda ana bilgisayar / yeniden yazma varsa özellikle yararlıdır:

Anahtar / değer değişimi gibidirler:

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

Ardından, kurallarınızda aşağıdaki gibi bir eşleme kullanabilirsiniz:

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

Bu konu hakkında daha fazla bilgi burada bulunabilir:

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


.htaccessTabanlı yeniden yazmalar kullanıyorsanız bu özelliği göz ardı edin. Bu bağlamda çalışmıyor.
TerryE

2
RewriteMap yönergesi sunucu bağlamında (httpd.conf) kullanılmalıdır, ancak burada tanımlandıktan sonra haritayı bir .htaccess dosyasında RewriteRule aracılığıyla kullanabilirsiniz.
JaredC

2

mod_rewrite, URL'yi değiştirmeden, örneğin ortam değişkenlerini ayarlamak, tanımlama bilgilerini ayarlamak vb.

Koşullu olarak bir ortam değişkeni ayarlayın:

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

503 tepkisini Dönüş: RewriteRule'in [R]bayrağı olmayan bir 3xx değerini almak ve yönetilen kesinti / bakım için örneğin olmayan bir yönlendirme yanıtı, dönebilirsiniz:

RewriteRule .* - [R=503,L]

503 yanıtı döndürecektir ( yönlendirme değil kendi başına değil).

Ayrıca mod_rewrite, mod_proxy için süper güçlü bir arabirim gibi davranabilir, böylece ProxyPassyönergeler yazmak yerine bunu yapabilirsiniz :

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

Görüş: RewriteRules ve kullanmaRewriteCond İsteklerinizi farklı uygulamalara veya yük dengeleyicilere yönlendirmek için, isteğin akla gelebilecek hemen hemen her yönüne dayalı olarak son derece güçlüdür. Arka uca giderken istekleri kontrol etmek ve geri dönüş yolunda yanıtları değiştirebilmek mod_rewrite'ı yönlendirmeyle ilgili tüm yapılandırmaları merkezileştirmek için ideal bir yer yapar.

Öğrenmek için zaman ayırın, buna değer! :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.