• Strona główna
  • Curriculum Vitae
  • O mnie
  • Przykład: Gramatyka w PHP
  • Przykład: Kompresja CSS
  • Przykład: Kompresja JavaScript
  • Przykład: Skracanie linków
  • Przykład: Wykrywanie serwera HTTP
  • Przykład: Własna bramka SMS
  • Mapa strony
  • Kontakt
Niebieski Pomarańczowy Zielony Różowy Fioletowy

Kiedy nie używać mod_rewrite?

Opublikowane 14 lutego 2010. Autor: Kamil Brenk. Wizyt: 7 192.

Kategorie: Apache
Tematyka: Apache, mod_alias, mod_proxy, mod_rewrite, mod_vhost_alias, serwery wirtualne, Wyrażenia regularne

lut 14

Moduł mod_rewrite to nie tylko „przyjazne” adresy URL. mod_rewrite jest modułem Apache, którego funkcjonalność jest przeogromna – o czym często nawet administratorzy serwerów nie wiedzą. A jeszcze częściej wiedzą i wykorzystują tam, gdzie nie powinni. Trzeba jednak pamiętać, że mod_rewrite to nie lekarstwo na każdy problem!

W poście tym zamierzam przedstawić kilka alternatywnych rozwiązań, które będą wygodniejsze w użyciu, bardziej optymalne oraz skalowalne.

Przepisywanie adresów URL

Pewnie trochę Cię to zdziwi, ale dlaczego nie zaleca się przepisywania linków za pośrednictwem tego modułu? Sposób ten jest prosty i pozwala w bardzo krótkim czasie stworzyć „przyjazne” linki dla całego serwisu.

Jednak uzależnianie się od jednego serwera (w tym przypadku Apache) jest błędem. Twój serwis powinien być skalowalny, a to znaczy, że powinno być możliwe bezproblemowe przeniesienie serwisu na inny serwer HTTP (np. IIS, nginx czy Lighttpd). Wystarczy, że nowy serwer nie będzie obsługiwał mod_rewrite i zabawa przepisywania linków zaczyna się od nowa :)

Wykorzystywanym przeze mnie rozwiązaniem jest stosowanie ustawień podobnych do tych oferowanych przez WordPress, tzn.:

1
2
3
4
5
6
7
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

W ten sposób każde odwołanie trafia do pliku index.php, w którym następnie specjalna klasa tłumaczy i przyporządkowuje „przyjazne” linki do rzeczywistych im adresów. Efekt taki sam jak w przypadku mod_rewrite, ale skalowalność dużo większa.

Zwykłe przekierowania

W wielu miejscach w sieci można poczytać o przekierowaniach za pośrednictwem .htaccess. Prezentowane przykłady opierają się na mod_rewrite i dyrektywie RewriteRule z flagą R.

Dlaczego ten sposób jest niezalecany? Gdyż od przekierowywania są specjalne reguły rozpoznawane przez serwer Apache, typu Redirect, RedirectMatch czy ProxyPass.

  1. Redirect
    Pozwala na wykonywanie prostych przekierowań – metoda ta jest najszybsza i pozwala na wykonywanie przekierowań typu jeden-do-jednego. Przykładowe zastosowania:
    1
    Redirect 301 / http://nowy.adres.pl/
    1
    Redirect pliki/ http://nowy.adres.pl/pliki/
  2. RedirectMatch
    Reguła podobna do powyższej, lecz powala na stosowanie bardziej skomplikowanych przekierowań, budowanych przy pomocy wyrażeń regularnych. Przykładowe zastosowanie:
    1
    RedirectMatch 301 (.*)\.jpg http://nowy.adres.pl/zdjecia/$1
  3. ProxyPass
    Reguła ta pozwala na wykonywanie przekierowań z wykorzystaniem serwera proxy. Przekierowania takie wykorzystują moduł mod_proxy i mają zastosowanie między innymi przy rozdzielaniu obciążenia na kilka serwerów. Przykładowe zastosowanie:
    1
    2
    3
    4
    5
    6
    7
    8
    <IfModule mod_proxy.c>
    # bezpieczeństwo - wyłaczenie pośrednictwa zwrotnego
    ProxyRequests Off

    # właściwe przekierowanie niewidoczne dla użytkownika
    ProxyPass / http://nowy.adres.pl/
    ProxyPassReverse / http://nowy.adres.pl/
    </IfModule>

    Chcąc wykorzystać mod_proxy do bardziej skomplikowanych operacji (np. pobranie obrazków z innego serwera niewidoczne dla klienta) można połączyć mod_proxy z mod_rewrite. Wygląda to następująco:

    1
    2
    3
    RewriteEngine On
    RewriteRule (.*\.jpg) http://zdjecia.strona.pl/$1 [P]
    ProxyPassReverse / http://zdjecia.strona.pl/
Serwery wirtualne

Moduł mod_rewrite można zastosować także do dynamicznego tworzenia serwerów wirtualnych. Jednak przed takim wykorzystaniem modułu warto rozważyć alternatywne rozwiązania. Istnieją bowiem dedykowane moduły utworzone specjalnie na cele serwerów wirtualnych. Mowa tutaj o mod_vhost_alias. Oczywiście warto wspomnieć o standardowych serwerach wirtualnych, domyślnie obsługiwanych przez serwer Apache.

Na korzyść mod_vhost_alias przemawia między innymi fakt, iż moduł ten pozwala odwzorowywać nazwy domenowe na nazwy katalogów. Umożliwia to dodawanie nowych nazw domenowych bez zmian w pliku konfiguracyjnym – czego nikt nie lubi.

Kontrola dostępu na podstawie adresu

Moduł mod_rewrite dzięki swojej warunkowej dyrektywie RewriteCond pozwala efektywnie blokować dostęp wybranym adresom IP, między określonymi godzinami czy dla różnych agentów przeglądarek. Jednak rzadko kiedy faktycznie wykorzystywane są powyższe sposoby blokowania treści, a najczęściej zabezpieczana jest jedynie wybrana część serwisu.

W tym celu nie należy korzystać z tego modułu. Wystarczającym rozwiązaniem jest zastosowanie serwerowych dyrektyw allow i deny dla danych folderów i miejsc na serwerze.

1
2
3
4
5
 <Directory /www/admin/>
     Order allow,deny
     Allow from 121.0.
     Deny from all
 </Directory>

Lub w bardziej skomplikowanym przypadku (z wykorzystaniem wyrażeń regularnych):

1
2
3
4
 <DirectoryMatch "^/www/.*/[0-9]{3}">
     Order allow,deny
     Allow from all
 </Directory>
Podsumowanie

Warto pamiętać, że moduł mod_rewrite został napisany w 1996 roku i nie zawsze może być najlepszym rozwiązaniem. Wiele innych rozwiązań pozwala na efektywniejsze zarządzanie serwerem i rzadko kiedy tak naprawdę potrzebujesz tego modułu.

Moja rada jest więc następująca: zamiast upatrywać się wszelkich miejsc, gdzie tylko możesz zastosować mod_rewrite, zacznij korzystać z tego modułu jedynie w sytuacjach bez wyjścia – jako ostatnią deskę ratunku. Powodów jest kilka, między innymi wykorzystywanie niewydajnych wyrażeń regularnych czy niepotrzebne aktywowanie kolejnego modułu, co również wydłuża działanie serwera Apache.

Komentarze (14)

  1. Michal Wachowski 14 lutego 2010

    Używam podobnego rozwiązania z pierwszego przykładu – różnica jest znikoma.

    1
    2
    3
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ ?furl=$1&%{QUERY_STRING}

    Jednym if’em załatwiam sprawdzane czy jest friendly czy nie.

    Nie chcę siać fermentu – ale część rzeczy nie jest realizowana przez ModRewrite – po prostu możliwości .htaccess są przeogromne.

    I jeszcze taka mała uwaga – RewriteBase nie jest wymagany, czasem przydatny, czasem szkodzi.

  2. Kamil Brenk 15 lutego 2010

    Właśnie w tej notce chciałem opisać miejsca, w których nadużywa się modułu mod_rewrite. Jest wiele innych modułów i dyrektyw Apache, które działają optymalniej (choćby przez niewykorzystywanie niewydajnych wyrażeń regularnych). Z tego co zauważyłem, zwłaszcza wśród pozycjonerów i przekierowań – ich problemy możnaby w dużo prostszy sposób rozwiązać (Redirect, RedirectMatch).

     

    1
    RewriteRule ^(.*)$ ?furl=$1&%{QUERY_STRING}

    Co do Twojej regułki mam pewną uwagę: wzorzec o wartości ^(.*)$ wymusza na mechanizmie wyrażenia regularnego przeanalizowanie całego ciągu tekstowego i porównanie każdego znaku z symbolem wieloznaczności . w celu sprawdzenia, czy znak pasuje do wzorca. Oczywiście każdy znak pasuje do wzorca . – jednak ciąg o n znakach będzie powodował wykonanie n operacji porównania.

    W przypadku wzorca z WordPress sprawa wygląda następująco:

    1
    RewriteRule . /index.php [L]

    Czyli od razu cała treść adresu URL jest przekazana do pliku index.php :)

  3. Michal Wachowski 15 lutego 2010

    Różnice są w pobieraniu wywołanego adresu

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // index.php znajduje się w http://domena.com/katalog/
    // $_GET['furl'] - moja wersja

    // dla adresu http://domena.com/katalog/pl/artykuly/tytul.html
    $HTTP_SERVER_VARS['PATH_INFO'] # nic
    $_SERVER['REQUEST_URI'] # /katalog/pl/artykuly/tytul.html
    $_SERVER['PATH_INFO'] # nic
    $_GET['furl'] # /pl/artykuly/tytul.html

    // dla adresu http://domena.com/katalog/index.php/pl/artykuly/tytul.html
    $HTTP_SERVER_VARS['PATH_INFO'] # /pl/artykuly/tytul.html
    $_SERVER['REQUEST_URI'] # /katalog/index.php/pl/artykuly/tytul.html
    $_SERVER['PATH_INFO'] # /pl/artykuly/tytul.html
    $_GET['furl'] # nic

    Możliwe, że coś pominąłem…

    Enyłej, po wpisaniu ‚php frienldy url’ w googlach, wyniki są … masakrujące.
    Tutoriale jak stworzyć potworki pokroju index.php/pl/artykul/tytul.html, albo przygotowywanie wyrażenia regularnego dla każdego wariantu adresu…
    Heh! Że też im się chce…

  4. piochu 2 stycznia 2011

    Cześć,
    a co wiesz na temat tego jak mod_rewrite wpływa na obciążenie serwera?
    pytam dlatego że chce zrealizować takie zadanie: za pomocą m_r zablokować możliwość pobierania z serwera plików tj. jpg, js, gif itd.
    Aktualnie używam do tego takiej regułki:

    RewriteRule \.(gif|jpg|jpeg|png|js|)$ /dev/null

    no i pytanie, jest to optymalne czy nie?

    Pozdrawiam :)

  5. Kamil Brenk 2 stycznia 2011

    @piochu: Jak nie mod_rewrite to czym innym chciałbyś blokować? Na pewno dużo więcej transferu zaoszczędzisz w ten sposób niż gdybyś zezwalał na pobieranie tych plików graficznych.

    Jeśli wydajność jest tutaj mega istotna to grafiki wydzielić do jednego folderu (grupy folderów / serwera) i na ten folder dać .htaccess z regułką jak powyższa.

  6. piochu 2 stycznia 2011

    Może wyjaśnie o co chodzi, tak będzie prościej.
    Pisze kawałek kodu który będzie analizował ruch na mojej stronie. Jeśli liczba odsłon na minute/godzinę (jak tam sobie ustale) wzrośnie o ustalony wskaźnik skrypt przełączy się w „tryb oszczędności transferu” ;), ma to być coś w rodzaju zabezpieczenia przed efektem wykopu.
    Czyli, jeśli link do mojej strony dostanie się na jakiś popularny serwis algorytm „zauważy to” i odpowiednio zareaguje.

    Myślę że dobrym wyjściem w takiej sytuacji (jeśli korzysta się z taniego, współdzielonego hostingu o ograniczonym transferze i chce się aby strona możliwie jak najdłużej przetrwała online) jest nie serwowanie ciężkiego contentu (zdjęcia, filmy itd.).
    Dlatego właśnie pytam czy regułka którą podałem nadawała by się do zastosowania w takiej sytuacji?

    Jeśli bredzę to powiedź ;)

  7. Kamil Brenk 3 stycznia 2011

    Niby dobre rozwiązanie, choć często JS w 80% jest pobierane z CDN Google czy innej serwerowni (mowa o głównej bibliotece, jak jquery czy prototype, które często ważą najwięcej). To samo z obrazkami – jeśli spodziewasz się tak wielkiego ruchu to można przerzucić na jakiś CDN i po problemie.

    Wyłączenie grafiki i innych bajerów też jest dobrym rozwiązaniem, choć przy większym natężeniu ruchu serwer i tak padnie :-) mod_rewrite też nie należy do najwydajniejszych rozwiązań i warto by było wyłączyć całkowicie przepisywanie adresów na ten czas. Zamiast tego mógłbyś w folderze z grafiką i plikami JavaScript umieścić plik .htaccess o treści:

    1
    2
    Order deny,allow
    Deny from all
  8. piochu 3 stycznia 2011

    ooo „mod_rewrite też nie należy do najwydajniejszych rozwiązań i warto by było wyłączyć całkowicie przepisywanie adresów na ten czas.” ooo takie stwierdzenie mi chodziło :)

    ogólnie, thx za info :)

  9. Piotr Nalepa 2 listopada 2011

    Właśnie odnalazłem Twój art w sieci. Na pewno się przydadzą Twoje informacje.
    Pozdrawiam

  10. Marek 17 lutego 2013

    Robiłeś coś może z przekierowaniami https?
    Usilnie staram się zrobić za pomocą mod_rewrite’a przekierowanie na panel admina po https i jakość kiszka :)

  11. Kamil Brenk 17 lutego 2013

    @Marek: nie robiłem takiego przekierowania, ale nie widzę w tym większych problemów. Poza tym sieć jest pełna materiałów o tym jak robić takie przekierowania. Może sprecyzuj swój problem oraz poprzyj przykładowym kodem, który nie działa? :)

  12. at0mic 21 lutego 2013

    mod_rewrite.so bo tak się ta biblioteka nazywa pod Linuksem i pod Windows również (nie .dll) to program napisany w języku c, skompilowany z opcją optymalizacji pod nowoczesne procesory i korzystający z pliku .htaccess (lub odpowiednio innego) jako plik konfiguracyjny a nie interpretowany język programowania.

    Co to oznacza?

    Ano to że plik konfiguracyjny modułu jest parsowany w jednym przebiegu a jego wartości podstawiane są pod konkretne miejsca w pamięci RAM bloku danych modułu mod_write.so nie jak w PHP gdzie składnia jest rozpoznawana i parsowana w 4-12 przelotach stosownie do zagnieżdżeń.

    Na procesorach pentium 3 ta operacja jest szybsza od jakiegokolwiek skryptu PHP o jakieś 200 razy a na i3 w trybie 64bit około 500 mimo że i parsowanie PHP przyspiesza. Niestety programy skompilowane do języka maszynowego tak mają że są szybsze od języków interpretowanych jakim jest np PHP.

    Skrypt PHP będzie na 100% wolniejszy przynajmniej o rząd wielkości od działania modułu mod_rewrite.so a zajętość pamięci będzie 1000 razy większa przy tworzeniu sterty wywołania kodu PHP niż 64KB pamięci które zajmuje moduł.

    To tyle. Czekam na konstruktywną krytykę.
    Serdecznie pozdrawiam!

  13. Kamil Brenk 25 lutego 2013

    @at0mic: bardzo słuszne uwagi – im więcej rzeczy przerzucimy na mod_rewrite tym szybciej nasza strona zadziała.

    Problem w tym, że regułki z .htaccess są przerabiane przy każdym żądaniu, niezależnie czy jest to plik graficzny czy czcionka, styl CSS czy żądanie zwykłej strony HTML. Zatem przy stronie zawierającej kilkadziesiąt plików (obrazki, css, javascript, czcionki) mod_rewrite sprawdzi każde żądanie z osobna pod kątem regułek zawartych w ustawieniach serwera. A jeśli do .htaccess władujemy kilkaset wyrażeń regularnych dla obsługi wszystkich podstron to trochę się nam mnoży tych zadań :-)

    Nie mówiąc o sytuacji, w której później zechcemy zmienić serwer Apache na nginx czy inny. Albo gdy klient zarządza możliwości ustawiania adresów URL z poziomu CMS.

  14. yaro 15 października 2018

    Jak zmienić re_write znak „_” na „-„?



Kamil Brenk Blog

PHP, JavaScript, SQL, HTML

  • Informacje o blogu

    Kamil Brenk

    Blog o tworzeniu aplikacji na potrzeby sieci Web.

    Praktyczne przykłady, porady i sztuczki. PHP, SQL, AJAX, JavaScript, HTML i pochodne.

    Kanał RSS

    • Najnowsze
    • Komentarze
    • Popularne
    • Liczniki w CSS
    • Wyprzedaż książek o programowaniu!
    • Niestandardowy placeholder
    • JavaScript w modułach
    • Co dalej z blogiem?
    • Interaktywna mapa w HTML i CSS
    • Olsztyn: Jak wyseparować zawartość zassaną przez file_get_content?
    • ERMLAB: Od czegoś trzeba zacząć :) Wiele osób właśnie stawia na...
    • david: co nalezy wkleić na stronę aby plik ze stylami był ladowany...
    • krynicz: Nie jestem pewien czy dobrze to rozumiem: wpisujemy OG w...
    • yaro: Jak zmienić re_write znak "_" na "-"?
    • Piotr: stworzyłem prostą stronkę w PHP, czy jest możliwość aby...
    • MichalR: Super sprawa... bardzo przydatne.. dzieki i pozdrawiam..
    • Niestandardowe czcionki na stronie
    • Sposoby wczytywania JavaScript
    • Gramatyka w PHP, część 1
    • Umowa i zaliczka dla freelancera
    • Wysyłanie wiadomości SMS w PHP
    • Projekt aplikacji po stronie klienta
    • Własny mechanizm Feed
  • Szukajka
    Wpisz co chcesz wyszukać na stronie…
  • Kategorie
    • Apache
    • Freelancer
    • Front-end Development
    • HTML5 & CSS3
    • Inne
    • JavaScript
    • Książki
    • PHP
    • Po godzinach
    • Pozycjonowanie
    • Protokół HTTP
    • SQL
    • Wyrażenia regularne
  • Moje serwisy
    • Testy zawodowe
    • Miłość, uczucia i seks
  • Czytane blogi
    • Wojciech Sznapka
    • Wojciech Soczyński
    • Michał Wachowski
    • Tomasz Kowalczyk
    • Filip Górczyński
  • Strona główna
  • Curriculum Vitae
  • O mnie
  • Przykład: Gramatyka w PHP
  • Przykład: Kompresja CSS
  • Przykład: Kompresja JavaScript
  • Przykład: Skracanie linków
  • Przykład: Wykrywanie serwera HTTP
  • Przykład: Własna bramka SMS
  • Mapa strony
  • Kontakt

Kamil Brenk © 2010. All rights reserved.

Designed by FTL Wordpress Themes brought to you by Smashing Magazine.

Do góry ∧