Ukryte funkcje mod_rewrite

119

Wydaje się, że mod_rewriteostatnio pojawia się przyzwoita liczba wątków z pewnym zamieszaniem co do tego, jak działają niektóre aspekty. W rezultacie zebrałem kilka uwag na temat typowych funkcji i być może kilka irytujących niuansów.

Jakie inne funkcje / typowe problemy napotkałeś podczas używania mod_rewrite?

Owen
źródło
5
Zobacz także serverfault.com/questions/214512/…
Michael Myers

Odpowiedzi:

203

Gdzie umieścić zasady mod_rewrite

mod_rewritereguły mogą być umieszczone w httpd.confpliku lub w .htaccesspliku. jeśli masz dostęp httpd.conf, umieszczenie reguł w tym miejscu przyniesie korzyści wydajnościowe (ponieważ reguły są przetwarzane raz, a nie za każdym razem, gdy .htaccessplik jest wywoływany).

Rejestrowanie żądań mod_rewrite

Logowanie można włączyć z poziomu httpd.confpliku (w tym <Virtual Host>):

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

Typowe przypadki użycia

  1. Aby skierować wszystkie żądania do jednego punktu:

    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 
    

    Od Apache 2.2.16 możesz również używać FallbackResource.

  2. Obsługa przekierowań 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] 
    

    Uwaga : przekierowania zewnętrzne to niejawnie przekierowania 302:

    # 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. Wymuszanie SSL

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

    • [R]lub [redirect]- wymuś przekierowanie (domyślnie tymczasowe przekierowanie 302)
    • [R=301]lub [redirect=301]- wymuś trwałe przekierowanie 301
    • [L]lub [last]- zatrzymaj proces przepisywania (patrz uwaga poniżej dotycząca typowych pułapek)
    • [NC]lub [nocase]- określ, że dopasowanie powinno nie uwzględniać wielkości liter


    Używanie długich flag jest często bardziej czytelne i pomoże innym, którzy przyjdą później przeczytać Twój kod.

    Możesz oddzielić wiele flag przecinkami:

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

Typowe pułapki

  1. Mieszanie mod_aliasprzekierowań zmod_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                 
    

    Uwaga : można zmieszać mod_aliasz mod_rewrite, ale to wymaga więcej pracy niż tylko podstawowe obsługi przekierowań jak wyżej.

  2. Kontekst wpływa na składnię

    W .htaccessplikach wiodący ukośnik nie jest używany we wzorcu 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] nie jest ostatni! (czasami)

    [L]Flag przestaje przetwarzać żadnych dalszych reguły przepisywania przez które przechodzą przez zestaw reguł . Jeśli jednak adres URL został zmodyfikowany w tym przebiegu i znajdujesz się w .htaccesskontekście lub <Directory>sekcji, to zmodyfikowane żądanie zostanie ponownie przekazane przez mechanizm analizy adresów URL. A przy następnym podaniu może tym razem pasować do innej reguły. Jeśli tego nie rozumiesz, często wygląda na to, że twoja [L]flaga nie przyniosła żadnego efektu.

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

    Nasz dziennik przepisywania pokazuje, że reguły są uruchamiane dwukrotnie, a adres URL jest aktualizowany dwukrotnie:

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

    Najlepszym sposobem obejścia tego jest użycie [END]flagi ( patrz dokumentacja Apache ) zamiast [L]flagi, jeśli naprawdę chcesz zatrzymać dalsze przetwarzanie reguł (i kolejne przebiegi). Jednak [END]flaga jest dostępna tylko dla Apache v2.3.9 + , więc jeśli masz wersję 2.2 lub niższą, utkniesz tylko z [L]flagą.

    W przypadku wcześniejszych wersji musisz polegać na RewriteCondinstrukcjach, aby zapobiec dopasowaniu reguł w kolejnych przebiegach mechanizmu analizowania adresów URL.

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

    Lub musisz upewnić się, że reguły RewriteRule znajdują się w kontekście (tj. httpd.conf), Który nie spowoduje ponownego przeanalizowania żądania.

Owen
źródło
10
Koleś, całkowicie najlepszy artykuł w Internecie teraz na przeróbce modów. Nienawidzę tego. Jestem heretykiem lighttpd z powodu tego, jak bardzo nienawidzę mod_rewrite.
Kent Fredric
3
To był NAJBARDZIEJ przydatny przewodnik, jaki do tej pory znalazłem na mod_rewrite. Samo zdobycie informacji o RewriteLog pomogło rozwiązać tak wiele problemów, że znalezienie tego, co zajmowało mi kilka dni, zajęło kilka minut. (Mam na myśli, że zasady zostały napisane, ale nie mogłem zrozumieć, dlaczego nie działają)
Joe Chin
Post sprzed 1 roku, ale jedna z bardziej przydatnych rzeczy, które znalazłem na SO - dla mnie.
Erik,
3
[L]Flaga oznacza zasada jest ostatni w bieżącym przetwarzania, to nie zatrzyma przepisywania, bo są przekierowania wewnętrzne, więc dirBstosuje się dirCw następnym przetwarzania htaccess. Samodzielnie RewriteRule ^(.*)$ index.php?query=$1będzie nieskończoną pętlą wewnętrznych przekierowań (w praktyce jest kończona po 10 iteracjach). -1, ponieważ sugerujesz, że [L] nie jest ostatnim . Nie kończy procesu przepisywania, ale jest ostatnia .
kbec
3
Uważam, że RewriteCond %{HTTPS} offjest to preferowany sposób sprawdzania połączenia HTTPS (w Twoim przykładzie wymuszania ruchu niezwiązanego z protokołem
SSL
22

jeśli chcesz `` zablokować '' wewnętrzne przekierowania / przepisywanie z pliku .htaccess, spójrz na

RewriteCond %{ENV:REDIRECT_STATUS} ^$

stan, jak omówiono tutaj .

mromainy
źródło
Dzięki, to właśnie rozwiązało mój problem!
Matthew
Dziękuję również za mnie, ratownik!
Benjamin,
To naprawdę ratuje życie! Ludzie powinni być tego bardziej świadomi. W rzeczywistości, mam zamiar zaproponować to na każde pytanie o .*z [L]flagą czytałem przed mam tutaj.
Qwerty
Widziałem kilka modyfikacji do tego 200, !=200, ^., ^$. Najwyraźniej zmienna zostaje ustawiona 200na przekierowanie, ale także inne strony (z błędami i tym podobne) ustawiają ją na jakąś wartość. Teraz to znaczy, że albo sprawdzić, czy to is empty, is not empty, is 200lub is not 200, w zależności od tego, co trzeba.
Qwerty
18

Umowa z RewriteBase:

Prawie zawsze musisz ustawić RewriteBase. Jeśli tego nie zrobisz, apache zgaduje, że twoją bazą jest fizyczna ścieżka dysku do twojego katalogu. Więc zacznij od tego:

RewriteBase /
Sean McMillan
źródło
Ach. To całkowicie rozwiązało problem, który miałem. Dziękuję za to!
Tom Savage,
3
Jakiś sposób powiedzieć RewriteBase .lub coś wskazującego, że powinien zachować ten sam adres URL, zmieniając tylko to, co określono?
Jay K
Dziękuję, to była bezcenna informacja. :)
AturSams
2
Musisz to ustawić tylko RewriteBasewtedy, gdy używasz względnego zastępowania ścieżki w RewriteRuledyrektywie. Lepiej jest unikać używania ścieżek względnych.
MrWhite
2
Nie zgadzam się z tą odpowiedzią. W naszym zespole programistów RewriteBasecałkowicie unikamy, ponieważ prawie wszyscy programiści źle rozumieją, co robi. Jak powiedział @ w3d, potrzebujesz go tylko wtedy, gdy chcesz zapisać znaki i chcesz zastosować tę samą bazę do wszystkich swoich Reguł RewriteRules w jednym pliku. Twój kod będzie prawdopodobnie bardziej zrozumiały dla innych, jeśli go unikniesz.
Simon East
13

Inne pułapki:

1- Czasami warto wyłączyć MultiViews

Options -MultiViews

Nie jestem dobrze zorientowany we wszystkich możliwościach MultiViews, ale wiem, że psuje moje reguły mod_rewrite, gdy jest aktywny, ponieważ jedną z jego właściwości jest próba „zgadnięcia” rozszerzenia pliku, którego według niego szukam .

Wyjaśnię: załóżmy, że masz 2 pliki php w swoim katalogu internetowym, plik1.php i plik2.php i dodajesz te warunki i regułę do swojego .htaccess:

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

Zakładasz, że wszystkie adresy URL, które nie pasują do pliku lub katalogu, zostaną przechwycone przez plik1.php. Niespodzianka! Ta reguła nie jest honorowana dla adresu URL adresu http: // mojhost / plik2 / somepath . Zamiast tego zostajesz przeniesiony do pliku2.php.

Chodzi o to, że MultiViews automagicznie odgadło, że adres URL, którego faktycznie potrzebujesz, to http: //myhost/file2.php/somepath i chętnie Cię tam zaprowadził.

Teraz nie masz pojęcia, co się właśnie wydarzyło, i w tym momencie kwestionujesz wszystko, co myślałeś, że wiesz o mod_rewrite. Następnie zaczynasz bawić się regułami, próbując zrozumieć logikę stojącą za tą nową sytuacją, ale im więcej testujesz, tym mniej ma to sensu.

Ok, w skrócie, jeśli chcesz, aby mod_rewrite działał w sposób zbliżony do logiki, wyłączenie MultiViews jest krokiem we właściwym kierunku.

2- Włącz FollowSymlinks

Options +FollowSymLinks 

Nie znam szczegółów tego, ale widziałem go wielokrotnie, więc po prostu zrób to.

Michael Ekoka
źródło
Dzięki :) Zauważyłem nieoczekiwane niespodzianki, takie jak / log / activity zamieniający się w /log.txt/activity .. Dzięki za wskazówkę :) .. szkoda, że ​​komputery nigdy nie bawią się nieoczekiwanymi rzeczami, takimi jak przypadkowe uwodzenie wszystkich współpracownic na Facebooku :)
AturSams
1
+FollowSymLinksjest wymieniony w dokumentacji jako obowiązkowy, mod_rewriteaby w ogóle pracować, z niejasnych względów bezpieczeństwa.
Joey
Martwią mnie dwa stwierdzenia: „Nie znam wszystkich funkcji MultiViews, ale wiem, że powoduje to bałagan w moich regułach mod_rewrite, gdy jest aktywny”, a to jedno „Ten, naprawdę nie znam szczegółów , ale wielokrotnie o tym wspominałem, więc po prostu zrób to. Chciałbym, żeby ludzie tacy jak ty nie pisali na SO odpowiedzi na tematy, których nie jesteś pewien.
TheCarver
1
@PaparazzoKid: Myślę, że mylisz SO z encyklopedią. To społeczność ludzi, którzy spotykają się, aby zrozumieć technologię, z którą pracują. W przeciwieństwie do AW White'a i Joey'a przed tobą, twój komentarz jest prawie pozbawiony wartości. MV i FSL to dwie z wielu opcji Apache. Moja odpowiedź dotyczy pułapek podczas pracy z mod_rw, w szczególności oddzielnym modułem, który koliduje z niektórymi opcjami, a działa z innymi. Wyjaśniłem, jak MV wpływa na mod_rw i wspomniałem, że + FSL jest popularną rekomendacją. Joey potwierdził, że w rzeczywistości jest to obowiązkowe. Co przynosisz do stołu?
Michael Ekoka,
Dzięki. Właśnie spędziłem większą część godziny, pracując nad starszą witryną i próbując debugować reguły przepisywania, ale okazało się, że MultiViews nadpisuje to wszystko.
Andrew McCombe
5

Równanie można wykonać na następującym przykładzie:

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]

Dynamiczne równoważenie obciążenia:

Jeśli używasz mod_proxy do zrównoważenia systemu, możesz dodać dynamiczny zakres serwera roboczego.

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

Konieczne jest lepsze zrozumienie flagi [L]. Flaga [L] jest ostatnia, musisz tylko zrozumieć, co spowoduje, że Twoje żądanie zostanie ponownie skierowane przez silnik analizujący adresy URL. Z dokumentacji ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (wyróżnienie moje):

Flaga [L] powoduje, że mod_rewrite przestaje przetwarzać zestaw reguł. W większości kontekstów oznacza to, że jeśli reguła pasuje, dalsze reguły nie będą przetwarzane. Odpowiada to ostatniemu poleceniu w Perlu lub poleceniu break w C. Użyj tej flagi, aby wskazać, że bieżąca reguła powinna zostać zastosowana natychmiast, bez rozważania dalszych reguł.

Jeśli używasz RewriteRule w plikach .htaccess lub w <Directory>sekcjach , ważne jest, aby mieć pewne zrozumienie, w jaki sposób reguły są przetwarzane. Uproszczona forma polega na tym, że po przetworzeniu reguł przepisany wniosek jest zwracany do silnika analizującego adresy URL, aby zrobić z nim to, co może. Jest możliwe, że po obsłużeniu przepisanego żądania, plik lub <Directory> sekcja .htaccess może zostać ponownie napotkany, a tym samym zestaw reguł może zostać uruchomiony ponownie od początku. Najczęściej dzieje się tak, gdy jedna z reguł powoduje przekierowanie - wewnętrzne lub zewnętrzne - powodując rozpoczęcie procesu żądania od nowa.

Zatem flaga [L] nie przestaje przetwarzać dalszych reguł przepisywania dla tego przebiegu przez zestaw reguł. Jeśli jednak reguła oznaczona [L] zmodyfikowała żądanie i znajdujesz się w kontekście .htaccess lub<Directory> sekcji, to zostanie ponownie przekazane przez mechanizm analizy adresów URL. A przy następnym podaniu może tym razem pasować do innej reguły. Jeśli nie rozumiesz, co się stało, wygląda na to, że twoja pierwsza reguła przepisywania z flagą [L] nie przyniosła żadnego efektu.

Najlepszym sposobem obejścia tego jest użycie flagi [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) zamiast flagi [L], jeśli naprawdę chcesz przestać dalsze przetwarzanie reguł (i późniejsze naprawy). Jednak flaga [END] jest dostępna tylko dla Apache v2.3.9 +, więc jeśli masz wersję 2.2 lub starszą, utkniesz tylko z flagą [L]. W takim przypadku należy polegać na instrukcjach RewriteCond, aby zapobiec dopasowaniu reguł w kolejnych przebiegach mechanizmu analizowania adresów URL. Lub musisz upewnić się, że Twoja Reguła RewriteRule znajduje się w kontekście (np. Httpd.conf), który nie spowoduje ponownego przeanalizowania żądania.

JaredC
źródło
3

Kolejną świetną funkcją są rozszerzenia mapy-przepisywania. Są szczególnie przydatne, jeśli masz ogromną liczbę hostów / przeróbek do obsłużenia:

Są jak klucz-wartość-zamiennik:

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

Następnie możesz użyć mapowania w swoich regułach, takich jak:

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

Więcej informacji na ten temat można znaleźć tutaj:

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

BYĆ
źródło
Zignoruj ​​tę funkcję, jeśli używasz .htaccessprzepisywania opartego na zasadach. To nie działa w tym kontekście.
TerryE
2
Dyrektywa RewriteMap musi być używana w kontekście serwera (httpd.conf), ale po jej zdefiniowaniu można używać mapy za pośrednictwem reguły RewriteRule w pliku .htaccess.
JaredC
2

mod_rewrite może modyfikować aspekty obsługi żądań bez zmiany adresu URL, np. ustawianie zmiennych środowiskowych, ustawianie plików cookie itp. Jest to niezwykle przydatne.

Warunkowo ustaw zmienną środowiskową:

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

Zwróci odpowiedź 503: RewriteRule„s [R]flag może przyjmować wartość non-3xx i zwracają non-przekierowanie odpowiedzi, np zarządzanej przestojów / konserwacji:

RewriteRule .* - [R=503,L]

zwróci odpowiedź 503 (a nie przekierowanie jako takie ).

Ponadto mod_rewrite może działać jak interfejs o dużej mocy dla mod_proxy, więc możesz to zrobić zamiast pisać ProxyPassdyrektywy:

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

Opinia: używając RewriteRules iRewriteCond s do kierowania żądań do różnych aplikacji lub modułów równoważenia obciążenia w oparciu o praktycznie każdy możliwy aspekt żądania jest po prostu niezwykle potężne. Kontrolowanie żądań w drodze do zaplecza i możliwość modyfikowania odpowiedzi w drodze powrotnej sprawia, że ​​mod_rewrite jest idealnym miejscem do centralizacji całej konfiguracji związanej z routingiem.

Poświęć trochę czasu, aby się tego nauczyć, naprawdę warto! :)

tydzień
źródło