Mod_rewrite - příklady

Petr Neuman

Toto není popis tohoto módu. Těchto pár příkladů je pouze doplňkem k úvodnímu návodu a snad poslouží jako zdroj inspirace či k pochopení některých vztahů.

Změna dynamických adres na statické

Následující použití ukazuje jednoduchý příklad přechodu na přepisované adresy. Původní adresy byly typu /index.php?page=neco - rádi bychom /neco.html skrytě přepsané na fungující skript. Na svém webu v první řadě opravíme odkazy na novou podobu, ale zachování odkazů z jiných webů a vyhledávačů (ale se snahou o propagaci nové podoby adres) vytvoříme přesměrování na nové adresy.

RewriteEngine On
#RewriteBase /adresar
RewriteCond %{QUERY_STRING} ^page=([^&]+)$
RewriteRule ^index\.php$ %1.html? [R=301,L,NE]
RewriteRule ^([^/]+)\.html index.php?rw=1&page=$1 [L,QSA]

Protože v RewriteRule nelze testovat parametry za otazníkem (query string), musíme použít pomínku RewriteCond s testováním této proměnné. Pokud má požadovaný tvar, bude kontrolovat následující pravidlo. V něm vytáhneme ozávorkovanou část z poslední použité podmínky díky %1. Za koncovku .html přidáme ještě onen otazník, aby se automaticky nepřidal query string. Další pravidlo je už klasické - skrytý přepis statické adresy na dynamickou. Přidáme však nějaký parametr (v našem případě rw=1), aby se přepisování necyklilo.

Odkomentujte druhý řádek s RewriteBase a napište tam správnou cestu adresáře za doménou. Pro kořenový adresář tedy použijte /
RewriteBase je důležité dobře nastavit vždy, když se používá flag R v kombinaci s relativními adresami. Při flagu R používejte i flag NE - noescape, při požadavku na adresu s nestandardními znaky v url (např. češtinou) by došlo k escapování již escapovaných parametrů.

Zakázání některých souborů

Pokud máme na webu soubory, které tam vyžadujeme, ale nechceme, aby si je mohl někdo prohlížet, můžeme využít síly Rewrite módu. Například schovat všechny soubory v adresářích CVS tím, že server vrátí kód 403 - zakázané.

RewriteEngine On
RewriteRule ^(.*/)?CVS/.* - [F]

Onen příznak F znamená Forbidden. Pomlčka je speciální znak pro zachování počtu parametrů jedlotlivých příkazů bez hlubšího smyslu (u RewriteRule tedy znamená něco jako "nech být, jak to je").

Zakázání parametrů některým robotům

Na zakázání přístupu robotů do určitých částí stránek můžeme vcelku s úspěchem použít robots.txt. Pokud ale chceme zakázat třeba jen některé parametry pro naše skripty, s robots.txt toho nedosáhneme. Můžeme tedy testovat, zda jde o roboty, kteří nám při takových parametrech dělají neplechu a například indexují i stránky, kde je v meta elementu robots nastavena hodnota noindex. Následují příklad znepřístupní vyjmenovaným třem robotům jakoukoliv stránku s nastaveným parametrem od (který máme např. použitý pro listování).

RewriteEngine on
RewriteCond %{QUERY_STRING} ^(.*&)?od=.*$
RewriteCond %{HTTP_USER_AGENT} ^findlinks.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^cfetch.*$ [NC]
RewriteRule ^.*$ - [F]

První podmínka RewriteCond testuje, zda je v query stringu parametr od. Pokud je, testuje se další podmínka na podobu proměnné HTTP_USER_AGENT, zde je však důležitý příznak OR, který je logickou spojkou nebo. Ony čtyři řádky s RewriteCond tedy znamenají něco jako "pokud je tam parametr od a zároveň agent začíná na Jyxobot nebo findlinks nebo cfetch". Pokud tato složená podmínka platí, pošle server na jakýkoliv požadavek kód 403 - zakázáno (díky již známému příznaku F). Příznak NC v RewriteCond vypíná rozlišování velikosti písmen.

Převádění & na & v query stringu

Někteří hloupí vyhledávací roboti (kdysi např. SeznamBot) nepřevádí html entity v nalezených adresách tak, jak mají. Pokud tedy validně odělujete jednotlivé parametry pomocí znaku & (který se v html píše jako &) můžete použít následující přepisovací pravidla na odchytání takto chybných požadavků pro celý web na jednom místě.

RewriteEngine on
#RewriteBase  /adresar
RewriteCond %{QUERY_STRING} ^(.*)&(.*)$
RewriteRule ^(.*)$ $1?%1&%2 [E=newqs:%1&%2,N]
RewriteCond %{ENV:newqs} !^$
RewriteRule ^(.*)$ $1?%{ENV:newqs} [R=301,L,NE]

První podmínkou zkontrolujeme, zda se v query stringu vyskytuje &. Pokud se tam najde, nahradí se správným &. Přitom se udělá i to, že se nový query string uloží do proměnné newqs díky příznaku E. Další příznak N - next round - vyvolá projíždění přepisovacích pravidel znovu od začátku (pokud se ono RewriteRule provádělo). Toto se bude provádět do té doby, dokud tam už žádný výskyt & nebude. Pak přijde na řadu další pomínka RewriteCond, která kontroluje, zda je proměnná newqs neprázná. Pokud není prázná (tzn. byl tam kdysi &), provede se viditelné přesměrovací pravidlo s použitím opraveného query stringu uloženého do proměnné.

Na příznak N dávejte veliký pozor! Nikdy ho netestujte na ostrých serverech, ale jen u sebe doma a i tak hodně opatrně (ale důkladně). Nesmí se vám to zacyklit. Na tento příznak se totiž nevztahuje limit na počet tajných přepsání (výchozí RewriteOptions MaxRedirects=10), takže můžete zahltit apache a nejspíše se z toho dostane jen jeho restartem.

Opravení špatně vyparsované adresy

Když se uvádí URI v běžném textu, často se za nimi vyskytují interpunkční znaménka, závorky a uvozovky, které horší parsery mohou brát jako součást adresy. Otestujeme, zda je požadovaná stránka dostupná a pokud není, zkusíme případné divné znaky z konce odstranit.

RewriteEngine On
#RewriteBase  /adresar
RewriteCond %{QUERY_STRING} ^(.*[^\.\?\),!-"'])[\.\?\),!-"']+$
RewriteRule ^(.*)$ $1?%1 [R=301,L,NE]
RewriteCond %{REQUEST_FILENAME} !-F
RewriteRule ^(.*[^\.\?\),!-"'])([\.\?\),!-"'])+$ $1 [R=301,L,NE]

Kotrolujeme tedy nejdříve znaky . ? ) , ! - " ' na konci query stringu (asi víme, že je na webu nepoužíváme a můžeme je odstranit). Pokud na konci tyto podivné znaky jsou, viditelně přesměrujeme na verzi, kde budou oddělány. Další RewriteCond kontroluje pomocí -F, zda apache nějak najde základní požadavek bez query stringu. Pokud tomu tak není, bude se provádět následující RewriteRule, které v případě, že jsou na konci opět zmíněné znaky, viditelně přesměruje na verzi, v níž budou z konce odstraněny.

Budeme-li mít tedy v našem adresáři soubory test a test. ale nic jiného, dostaneme při požadavku na test. správně test., ale při požadavku na test.. se všechna interpukce na konci odstraní a přesměruje nás to na test bez tečky. Při žádosti o test..?querystring. se postupně provedou dvě přesměrování na jejichž konci bude test?querystring - nejdříve se odtranila tečka z query stringu a pak i dvě tečky ze jména souboru.

Rozložení zátěže na více počítačů

Za jistých okolností lze pomocí Rewrite módu tajně přepisovat i na jiné servery. Je však nutné k tomu mít v apachi zapnutý mod_proxy. Může se to hodit například k rozhození výpočetní zátěže na více našich strojů. Pro výběr stroje použijeme přepisovací mapu typu rnd (náhodnou) a to jen na soubory s koncovkami php, pl, py a rb, kde předpokládáme vyšší nároky na procesor.

#prepisovaci mapa musi byt v definovana v konfiguraci apache, v .htaccess to nelze
#RewriteMap servery rnd:/cesta/k/mapa.txt
RewriteEngine on
RewriteRule ^(.*\.(php|pl|py|rb)) http://${servery:nahodny}/$1 [P,L]

Definovat přepisovací mapu pomocí příkazu RewriteMap lze pouze v konfiguraci serveru nebo v nastavení virtual hostu. My použijeme typ rnd, který vybere náhodně jednu hodnotu ze seznamu ke konkrétnímu klíči. Ke shlédnutí je ukázka použité přepisovací mapy. Syntaxe použití mapy je jednoduchá, použijeme název mapy a hodnotu, kterou hledáme (lze případně i definovat hodnotu, která se má použít, pokud nebyl klíč v mapě nalezen). Příznak P říká, že chceme použít proxy modul a tím způsobem tajně přesměrovat na jiný server. Tento příklad nebyl zkoušen pro nedostatečné technické zázemí, ale je uváděn pro demonstraci málo známé možnosti Rewrite módu.

Ukládací dialog pro známé soubory

Občas se může hodit vyvolat stahovací dialog v prohlížeči i pro soubory, které umí normálně zobrazit. Můžeme pomocí RewriteRule změnit mime-type souboru třeba na application/octet-stream a tak docílíme toho, že přebijeme základní nastavení apache. Pokud budeme chtít soubor takto stáhnout, přidáme za něj parametr stahnout.

RewriteEngine on
RewriteCond %{QUERY_STRING} ^stahnout$
RewriteRule \.(jpe?g|gif|png)$ - [L,NC,T=application/octet-stream]

Pokud na server přijde požadavek na soubor s koncovku jpg, jpeg, gif nebo png a je přidán parametr stahnout (například obrazek.jpg?stahnout), zapříčiní uvedená pravidla změnu mime-typu a tím vyvolají dialog pro stáhnutí. Změnu typu vyvolá parametr T. Parametr NC vypíná rozlišování velikosti písmen v RewriteRule. Koncovky, kterým chceme povolit stahování je dobré vyjmenovat, aby nám díky tomuto parametru někdo nestahoval třeba zdrojáky v php (pokud totiž takto změníme typ souboru, bude s ním apache podle toho zacházet a nebude brát php soubor jako skript).

Různé robots.txt podle domény

Pokud chceme mít více domén se stejným obsahem generovaným z jednoho adresáře, může nám vadit, že vyhledávací systémy poznají, že se jedná o duplicity a některé věci ukazují z jiných adres, než bychom chtěli. Nachystáme si tedy několik různých souborů robots.txt, kterými zakážeme pro danou doménu přesně ty adresáře a soubory, které chceme (pokud možno vždy povolíme určité podstránky jen na jednom serveru).

RewriteEngine on
RewriteCond _%{HTTP_HOST}_robots.txt -F
RewriteRule ^robots\.txt$ _%{HTTP_HOST}_robots.txt [L]

Náš systém počítá s tím, že budou připraveny soubory vzoru _example.com_robots.txt nebo _127.0.0.1_robots.txt. Pokud bude odpovídat host v požadavku některé z nachystaných variant, podstrčí se jako robots.txt právě ona. Pokud je požadavek na robots.txt (interně se RewriteRule kontroluje dříve než RewriteCond) zkontroluje RewriteCond, zda má apache k dispozici příslušnou variantu našeho vzoru, pokud ano, tak ji další příkaz tajně podstrčí.

Zapnutí rewrite u minibb 2.0

Následující příklad je jen trošku složitější něž ten první. Pokud používáte minibb fórum, může se vám hodit - počínaje verzí 2.0 umí toto fórum použít odkazy, které vypadají jako html soubory. Pokud na něj přecházíme z nižší verze, nebo jsme jen zapomněli odkomentovat $mod_rewrite=TRUE; a chceme co nejvíce používat nové adresy, použijeme takto upravená pravidla, která zaručí zpětnou kompatibilitu.

RewriteEngine On
#RewriteBase /adresar

#parametry na kratkou html adresu
RewriteCond %{QUERY_STRING} ^action=userinfo&user=([0-9]+)$
RewriteRule ^index\.php$ user%1.html? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vtopic&forum=([0-9]+)$
RewriteRule ^index\.php$ %1_0.html? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vtopic&forum=([0-9]+)&page=([0-9]+)$
RewriteRule ^index\.php$ %1_%2.html? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vthread&forum=([0-9]+)&topic=([0-9]+)$
RewriteRule ^index\.php$ %1_%2_0.html? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vthread&forum=([0-9]+)&topic=([0-9]+)&page=([0-9]+)$
RewriteRule ^index\.php$ %1_%2_%3.html? [R=301,L,NE]

#html adresy tajne na puvodni (pridano rw=1, aby se to necyklilo)
RewriteRule ^user([0-9]+)\.html$ index.php?action=userinfo&user=$1&rw=1  [L]
RewriteRule ^([0-9]+)_([0-9]+)\.html$ index.php?action=vtopic&forum=$1&page=$2&rw=1  [L]
RewriteRule ^([0-9]+)_([0-9]+)_([0-9]+)\.html$ index.php?action=vthread&forum=$1&topic=$2&page=$3&rw=1  [L]

Pracuje to na stejném principu jako příklad na začátku, jen tam je prostě víc vztahů. V tomto příkladě není nutný pomocný parametr rw=1, stačilo by přeházet pořadí parametrů.

Zapnutí rewrite u minibb 2.0 s rewritemapou

Pokud umíme trochu php, tak si lze adresy v minibb upravit k obrazu svému. Místo čísel pro fórum použijeme nějaký výstižný popis. Převod mezi čísly a texty zvládneme pomocí RewriteMap. Vhledem k tomu, že už to bude složitější použití přepisovacíh pravidel, zkusíme procházení optimalizovat a co nejméně zatěžovat server přepisováním.

Cílem našeho přepisování budou tedy adresy typu
example.com/nas_nazev_fora
example.com/nas_nazev_fora/cislovlakna
pokud zapomeneme definovat vztahy mezi čísly a texty, tak typu
example.com/forum-cislo
example.com/forum-cislo/cislovlakna
a za to může být ještě přidáno /stranka-cislo pro případné stránkování.

RewriteEngine On
#RewriteBase /adresar

#primo v konfiguraci apache (napr. pro nas virtualni server) musime uvest prepisovaci mapy
#RewriteMap nototext txt:/cesta/k/mapa01.map
#RewriteMap texttono txt:/cesta/k/mapa02.map

#urychleni prochazeni pravidel - vyskoceni co nejdriv to jde
#pokud je k dispocici primo soubor bez prepisovani a neni to index.php, tak neprepisuj nic
RewriteCond %{REQUEST_FILENAME} !index\.php
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* - [L]
#pokud je na zacatku nastaveny nas parametr rw, tak pro index.php take vyskoc
RewriteCond %{QUERY_STRING} ^rw=1
RewriteRule ^index\.php$ - [L]
#pokud je to index.php, tak preskoc 7 nasledujich pravidel RewriteRule
RewriteRule ^index\.php$ - [S=7]
#konec urychlovani

#jinak pokracujeme

#pokud uz mame k foru s cislem popisek v rewrite mape, tak presmerovani
RewriteCond %{REQUEST_FILENAME} .*forum-([0-9]+)(/.*)?
RewriteCond ${nototext:%1|notfound} !=notfound
RewriteRule ^forum-([0-9]+)(/.*)?$ ${nototext:$1}$2 [R=301,L,NE]

#jestli je na konci stranka-x, hod x do promenne pro pozdejsi pouziti
RewriteRule /stranka-([0-9]+)$ - [E=stranka:&page=$1]
#nase adresy tajne na puvodni (pridano rw=1 aby se to necyklilo a umeli jsme rychle vypadnout)
RewriteRule ^forum-([0-9]+)(/stranka-[0-9]+)?$ index.php?rw=1&action=vtopic&forum=$1%{ENV:stranka} [L,QSA]
RewriteRule ^forum-([0-9]+)/([0-9]+)(/stranka-[0-9]+)?$ index.php?rw=1&action=vthread&forum=$1&topic=$2%{ENV:stranka} [L,QSA]
RewriteRule ^([^/\.-]+)(/stranka-[0-9]+)?$ index.php?rw=1&action=vtopic&forum=${texttono:$1}%{ENV:stranka} [L,QSA]
RewriteRule ^([^/\.-]+)/([0-9]+)(/stranka-[0-9]+)?$ index.php?rw=1&action=vthread&forum=${texttono:$1}&topic=$2%{ENV:stranka} [L,QSA]
RewriteRule ^user-([0-9]+)\.html$ index.php?rw=1&action=userinfo&user=$1 [L]

#kategorie/cislo_threadu
#parametry na kratkou html adresu - pro zpetnou kompatibilitu
RewriteCond %{QUERY_STRING} ^action=userinfo&user=([0-9]+)$
RewriteRule ^index\.php$ user-%1.html? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vtopic&forum=([0-9]+)(&sortBy=0)?(&page=0)?$
RewriteRule ^index\.php$ ${nototext:%1|forum-%1}? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vtopic&forum=([0-9]+)(&sortBy=0)?&page=([0-9]+)$
RewriteRule ^index\.php$ ${nototext:%1|forum-%1}/stranka-%3? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vthread&forum=([0-9]+)&topic=([0-9]+)(&sortBy=0)?(&page=0)?$
RewriteRule ^index\.php$ ${nototext:%1|forum-%1}/%2? [R=301,L,NE]
RewriteCond %{QUERY_STRING} ^action=vthread&forum=([0-9]+)&topic=([0-9]+)&page=([0-9]+)$
RewriteRule ^index\.php$ ${nototext:%1|forum-%1}/%2/stranka-%3? [R=301,L,NE]

První dvě podmínky RewriteCond nám otestují, zda požadovaný soubor není index.php a zároveň takový soubor opravdu existuje na serveru. Pokud obě podmínky platí, přestane se přepisovat díky RewriteRule s nic neměnící pomlčkou a příznakem L. Nyní testujeme, zda je v query stringu nastavený parametr rw=1. Pokud tomu tak je a žádaným souborem je index.php, také ukončíme všechno přepisování. Posledním testem pro optimalizaci pravidel je opět test, zda je na server požadavek o index.php, v kladném případě přeskočíme kontrolu náledujích 7 pravidel RewriteRule pomocí příznaku S.

Nyní testujeme, zda není požadavek na adresu typu forum-cislo a zda náhodou už pro toto číslo nemáme definovaný v přepisovací mapě textový popis. Syntaxe pro vycucnutí hodnoty z mapy je ${nazev_mapy:hledana_hodnota|hodnota_pokud_se_hledana_nenajde}. Otestujeme tedy, zda výsledná hodnota není ta, kterou použijeme jako výchozí za znakem |. Pokud tedy už máme pro ono číslo textový popis definovaný, viditelně přesměrujeme na novou adresu.

Následuje tajné přepisování našich zvolených adres na příslušné parametry minibb. Nejprve otestujeme, zda je na konci adresy stranka-cislo. Pokud je, uložíme do proměnné stranka řetezec, který se používá pro stránkování v minibb fóru. Dalších 5 pravidel přepisuje možné vzory adres na správné parametry. Přidáváme zde případné stránkování uložené do proměnné stranka a dále přidáme parametr rw=1, který nám umožní rychlejší opuštění znovuprocházení přepisovacích pravidel a zamezí tak cyklení.

Poslední sada pravidel nám přesměruje z kdysi používaných adres běžných v tomto fóru na nové typy adres. Asi je už jasné, že výsledek ${nototext:%1|forum-%1} bude buď textový popis fóra (pokud máme ono číslo definované v přepisovací mapě nototext) nebo forum-cislo (pokud v mapě ono číslo definované není).

Jak již bylo v zmíněno, přepisovací mapy lze definovat pouze v konfiguraci serveru nebo virtual hostu. Ještě ukázka těch použitých přepisovacíh map - mapa01.map a mapa02.map - jedná se o standardní typ mapy txt.

 

Autorem textu je Petr Neuman

 

Reklama

www.webhosting-c4.cz, extra rychlý SSD webhosting s doménou v ceně
o tvorbě, údržbě a zlepšování internetových stránek

Návody HTML CSS JavaScript Články Ostatní

Encyklopedie FrontPage Reklama PHP Server

Jak psát web píše Yuhů, Dušan Janovský. Kontakt.