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.
- Redirect
Pozwala na wykonywanie prostych przekierowań – metoda ta jest najszybsza i pozwala na wykonywanie przekierowań typu jeden-do-jednego. Przykładowe zastosowania:1Redirect 301 / http://nowy.adres.pl/1Redirect pliki/ http://nowy.adres.pl/pliki/ - RedirectMatch
Reguła podobna do powyższej, lecz powala na stosowanie bardziej skomplikowanych przekierowań, budowanych przy pomocy wyrażeń regularnych. Przykładowe zastosowanie:1RedirectMatch 301 (.*)\.jpg http://nowy.adres.pl/zdjecia/$1 - 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
3RewriteEngine 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.


Używam podobnego rozwiązania z pierwszego przykładu – różnica jest znikoma.
2
3
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.
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).
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:
Czyli od razu cała treść adresu URL jest przekazana do pliku index.php :)
Różnice są w pobieraniu wywołanego adresu
2
3
4
5
6
7
8
9
10
11
12
13
14
// $_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…