mod_rewrite – Martin Bartušek, Petr Šťastný
mod_rewrite Martin Bartušek Petr Šťastný MFF UK, 2008
Obsah 1.Apache a moduly...............................................................................................................................2 2.Přepisování........................................................................................................................................2 3.Instalace a nastavení modulu.............................................................................................................2 4.RewriteBase.......................................................................................................................................3 5.RewriteRule.......................................................................................................................................3 5.1Příklady.......................................................................................................................................4 6.RewriteCond......................................................................................................................................5 6.1Příklady.......................................................................................................................................6 7.RewriteMap.......................................................................................................................................9
1
mod_rewrite – Martin Bartušek, Petr Šťastný
1. Apache a moduly Apache je nejpopulárnější webový server. Existuje k němu velké množství doplňků (modulů), které rozšiřují jeho možnosti a funkčnost. Obsahem tohoto textu je popis zejména jednoho z nich – a to mod_rewrite. Mod_rewrite umožňuje za běhu serveru přepisovat URL adresy přicházejících požadavků. Překlad se uskutečňuje zpracováváním pravidel, která jsou zapsaná regulárními výrazy. Kromě pravidel se překlad může řídit i dalšími podmínkami. Překlad URL adresy může být následující: www.example.com?clanek.php?id=12342 ->
www.example.com/clanek/pocasi
S mod_rewrite souvisí další dva moduly pro apache: mod_alias a mod_proxy. Modul mod_alias pro změnu mapování mezi URL adresou objektů a fyzickým umístění na serveru. Oproti mod_rewrite nabízí méně možností – nepodporuje podmínky. Hodí se tedy na jednodušší přepisovaní – např. přesun root directory. Mod_proxy umožňuje apache serveru fungovat jako HTTP proxy server. Toho lze využít i v kombinaci mod_rewrite.
2. Přepisování URL adresu můžeme přepsat dvěma způsoby - přesměrováním nebo podstrčením. ●
Přesměrování – klientský prohlížeč obdrží HTTP odpověď redirect (HTTP kód 301 nebo 302) na nové umístění. Uživatel tedy zjistí, že se URL změnilo.
●
Podstrčení – server vrátí prohlížeči obsah „nové“ URL adresy, ale neinformuje ho o tom. Uživatel nezjistí, že k podstrčení došlo. Obsah může pocházet ze stejného webu, z jiného webu na stejném serveru nebo z úplně jiného serveru.
Podstatné je, kdy k přepisu URL adresy vlastně dochází. Děje se tak přijetí HTTP požadavku serverem (po parsování požadavku, hlavičky atd.), ale ještě předtím, než se požadovaná URL začne interpretovat (tj. než se začne hledat soubor, který se má uživateli poslat, nebo než se zavolá skriptovací engine, který má soubor spustit). Interpretuje se až přepsaná URL. Z toho vyplývá, že můžeme libovolným způsobem ovlivnit, co nakonec uživatel do svého prohlížeče dostane.
3. Instalace a nastavení modulu Modul mod_rewrite je součástí Apache serveru. Stačí v konfiguračním souboru serveru httpd.conf odkomentovat následující řádek (po úpravě konfiguračního souboru je nutné Apache restartovat, aby se změny projevily): LoadModule rewrite_module modules/mod_rewrite.so
Druhou možností je kompilace apache serveru s parametrem –enable-rewrite Po aktivaci modulu je potřeba napsat příslušná přepisovací pravidla. Pravidla mohou být napsána na dvou místech: ●
V konfiguračním souboru apache serveru (httpd.conf) – per-server nebo per-virtualhost
●
V souboru .htaccess, který umístíme do adresáře stránek. Tato možnost musí být povolena 2
mod_rewrite – Martin Bartušek, Petr Šťastný správcem serveru. Konfigurace v souboru .htaccess se pak vztahuje na adresář, ve kterém je, i všechny podadresáře. Samozřejmě v některém z podadresářů může být další .htaccess, který jej překryje. Pokud jsou přepisovací pravidla definována na několika místech, platí vždy ta nejblíž cíli původní URL adresy a nekombinují se s těmi nadřazenými (protože by to nemělo smysl). Dříve než začneme psát přepisovací pravidla, je nutné mod_rewrite zapnout pomocí: RewriteEngine on
4. RewriteBase Pomocí této direktivy nastavíme výchozí adresář pro cíle všech přesměrování, od kterého se pak odvozují relativní cesty. RewriteBase path
5. RewriteRule Tato direktiva označuje samotné přepisovací pravidlo a má následující syntaxi: RewriteRule Pattern Substitution [flags] Pattern je regulární výraz, kterým specifikujeme, kdy se má pravidlo provést. Syntaxe regulárního výrazu je obdobná jako v Perlu, navíc můžeme používat negaci (znak „!“ na začátku). Pattern se porovnává („matchuje“) s URL adresou, kterou klientský prohlížeč požaduje. Pokud pattern odpovídá, provede se přepis na substitution. Substitution – nová URL adresa stránky, která se klientovi opravdu zobrazí. Adresa může být buďto absolutní (začínající http nebo https) či relativní. Relativní cesta začínající lomítkem se odvozuje od rootu virtualhostu, v opačném případě od aktuálního adresáře (resp. od RewriteBase). V substitution se také můžeme odkazovat na „namatchované“ části regexpu (části ohraničené kulatými závorkami), systémové proměnné nebo mapovací funkce (vše bude vysvětleno dále v tomto textu). Odkaz na n-tou část patternu provedeme voláním $N. Je možno odkazovat se i na část patternu z podmínky RewriteCond (viz. dále) pomocí %N. Odkazovat na systémovou proměnnou se můžeme pomocí %{VARNAME} a mapovací funkci pomocí ${mapname:key|default}. V substitution lze také pracovat s parametry URL adresy. Jestliže substitution neobsahuje znak otazníku, na konec změněné URL se doplní původní parametr. Uvedením pouhého otazníku na konci se všechny parametry smažou. Za otazník můžeme napsat nové parametry, i zde můžeme použít namatchované části z patternu ($N a %N). Pokud navíc uvedeme příznak QSA (viz. dále), doplní se k novým parametrům i ty původní. Pokud je v konfiguraci uvedeno více klauzulí RewriteRule, zpracovávají se v pořadí, v němž jsou uvedeny. Jakmile pravidlo URL adrese vyhovuje, provede se přepis a další pravidla pracují s již přepsanou adresou (nikoliv s původní). Může tedy dojít k více přepisům, kdy se URL postupně 3
mod_rewrite – Martin Bartušek, Petr Šťastný transformuje. [flags] – nepovinné parametry. Mezi nejpoužívanější patří následující: ●
F – zakáže URL, vrací HTTP odpověď 403 (forbidden)
●
L – toto je poslední pravidlo, další se nebude provádět
●
NC – case-insensitive výraz
●
P – force proxy, vynucení zpracování cíle přes mod_proxy
●
QSA – na konec přidat původní URL parametry
●
R[=code] – přesměrovat na novou URL ○
301 – Moved Permanently
○
302 – Found (Moved Temporarily) – výchozí
5.1 Příklady
# obyčejné přesměrování RewriteRule puvodni-stranka1\.html nova-stranka1.html [R]
Jedná se o jednoduché přesměrování URL jedné stránky na jinou stránku na stejném webu. Typicky se použije v případě, kdy se změní adresa konkrétní stránky a chceme, aby klienti začali používat adresu novou. Zde je třeba dbát na to, že tečky a jiné znaky v regulárním výrazu je potřeba oescapovat, protože tečka v regulárním výrazu znamená libovolný znak. Druhý parametr již regulární výraz není, tam naopak tečku oescapovat nesmíme. Také zde musíme uvést příznak R, jinak by se implicitně provedlo podstrčení a nikoliv přesměrování (v tomto případě chceme, aby se uživatel dozvěděl novou adresu).
# obyčejné podstrčení RewriteRule puvodni-stranka2\.html nova-stranka2.html
Podobná situace, zde však chyby příznak R pro přesměrování. Protože druhý parametr je relativní adresa, implicitně se provede podstrčení. Uživateli se v prohlížeči URL adresa nezmění, do prohlížeče se mu však pošle obsah té nové.
# přesměrování jinam RewriteRule stranka\.html http://www.example.com/jinam.html
Zde dojde k implicitnímu přesměrování, protože druhým parametrem je absolutní URL.
4
mod_rewrite – Martin Bartušek, Petr Šťastný # zákaz stránek RewriteRule ^(.*/)?CVS/.* - [F] RewriteRule ^(.*/)?\.svn/.* - [F]
Příznak F se používá pro zákaz přístupu k některým zdrojům na webu. V tomto případě nechceme, aby se kdokoliv přes prohlížeč dostal k obsahu adresářů nástrojů CVS nebo Subversion. Není podstatné, zda takový adresář nebo nějaký soubor v něm existuje. Podstatné je, že URL adresa vyhovuje danému regulárnímu výrazu. U tohoto příznaku nemá smysl uvádět URL adresu ve druhém parametru, ale kvůli dodržení počtu parametrů je nutné napsat pomlčku.
# nastavení MIME-type dokumentu RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source]
Toto je trik, jak nabídnout návštěvníkům možnost prohlédnout si zdrojové kódy všech souborů s koncovkou php. Při požadavku na soubor s koncovkou phps se uživateli pošle do prohlížeče stejnojmenný soubor s koncovkou php se speciálním MIME typem (to je zejména proto, aby se php soubor neinterpretoval, tj. nespustil, ale zobrazil se jeho obsah).
# jazyk schovaný v URL RewriteRule ^cs/(.*)$ $1?lang=cs [QSA] RewriteRule ^en/(.*)$ $1?lang=en [QSA]
Tímto způsobem si můžeme v URL adrese přenášet důležité parametry (v tomto případě kód jazyka), pokud se nám nechce přenášet jako skutečný parametr na konci adresy.
# přesměrování všeho RewriteRule (.*) http://www.example.com/
6. RewriteCond Pomocí direktiv RewriteCond můžeme stanovit jednu nebo více podmínek, které musí být splněny, aby se aplikovalo následující pravidlo RewriteRule. RewriteCond TestString CondPattern [flags] TestString – testovaný řetězec, který budeme matchovat s CondPattern. Může obsahovat systémové proměnné nebo mapovací funkce. CondPattern – může být standardní regulární výraz s negací. Druhou možností je obyčejný řetězec písmen se speciálním významem. Můžeme využívat porovnání (<,>,= ), kdy dojde k lexikografickému porovnání s TestStringem. Dále můžeme využít následující příznaky, které vždy testují TestString. 5
mod_rewrite – Martin Bartušek, Petr Šťastný ●
-d – je TestString adresářem?
●
-f – je TestString souborem?
●
-s – je TestString neprázdným souborem?
●
-l – je TestString symbolickým linkem?
●
-x – má TestString executable právo?
Podmínka je tedy splněna, pokud TestString vyhovuje regulárnímu výrazu nebo jiné podmínce v CondPattern. Pokud je před RewriteRule uvedeno více klauzulí RewriteCond, musí být k provedení přepisu splněny všechny, tedy jako kdyby měly mezi sebou spojku AND (to lze změnit příznakem OR). [Flags] – u podmínky máme možnost využít 2 příznaky: ●
NC – case-insensitive výrazy
●
OR – následující podmínka je ve vztahu „OR“. Implicitně jsou podmínky ve vztahu „AND“
V RewriteRule (v Substitution) i RewriteCond (v TestString) lze používat systémové proměnné, zapisují se ve formátu %{NAZEV_PROMENNE}. K dispozici jsou například: ●
HTTP hlavičky - HTTP_USER_AGENT, HTTP_REFERER, HTTP_HOST, …
●
informace o spojení a požadavku - REMOTE_ADDR, REQUEST_METHOD, QUERY_STRING, …
●
serverové proměnné - DOCUMENT_ROOT, SERVER_NAME, …
●
datum a čas - TIME, TIME_YEAR, TIME_HOUR, TIME_WDAY, …
●
%{ENV:variable} – proměnné prostředí
●
%{SSL:variable} – parametry SSL spojení
●
%{HTTP:header} - libovolná HTTP hlavička
6.1 Příklady # jen pokud požadovaný soubor opravdu neexistuje RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^forum/topic-([0-9]+)\.html$ forum-topic.php?id=$1 [QSA,L]
Podstrčení jiné stránky se provede jen v případě, že původně požadovaný soubor neexistuje. Toto je také příklad pravidla pro vytváření „hezkých“ URL adres, kdy si parametr (např. číslo tématu diskuze) schováme přímo do názvu souboru a nevyskytuje se jako URL parametr. V tomto případě je potřeba u pravidla uvést příznak QSA, který na konec nové adresy doplní i původní URL parametry.
6
mod_rewrite – Martin Bartušek, Petr Šťastný # výchozí skript RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)\.html$ /unipage.php?page=$1 [L,QSA]
Pokud požadovaný soubor doopravdy existuje, je toto pravidlo přeskočeno a soubor se odešle do prohlížeče. Jestliže soubor neexistuje, předá se požadavek univerzálnímu skriptu, který může podle URL adresy najít příslušný obsah např. v databázi anebo oznámit chybu.
# přesměrování podle domény # example.com -> www.example.com RewriteCond %{HTTP_HOST} ^example\.com$ RewriteRule (.*) http://www.example.com/$1 [R=301]
Tato podmínka a pravidlo se postarají o to, že návštěvník, který přijde přes domény bez „www“, bude ihned přesměrován na stejnou stránku, ale na doméně s „www“. To je žádoucí, protože některé vyhledávače penalizují stránky za to, že je stejný obsah dostupný pod více URL adresami. Jako HTTP kód je zde uveden 301, který říká, že se jedná o trvalé přesměrování.
# přesměrování na HTTPS RewriteCond %{HTTPS} ^off$ RewriteRule (.*) https://%{HTTP_HOST}/$1 [R]
Toto nám zase zajistí to, že je uživatel vždy přesměrován na zabezpečené připojení přes SSL.
# různé verze RewriteCond RewriteCond RewriteRule RewriteRule
stránek podle data/času %{TIME_HOUR}%{TIME_MIN} %{TIME_HOUR}%{TIME_MIN} ^foo\.html$ ^foo\.html$
>0700 <1900 foo.day.html foo.night.html
Mnohá kouzla lze dělat podle aktuálního data a času. Využíváme toho, že v RewriteCond lze hodnoty porovnávat lexikograficky a že složky data a času jsou zarovnávány na stejnou délku (doplňovány zleva nulami). V tomto příkladu tedy návštěvníkovi podstrčíme verzi stránky podle denní doby. Zde je podstatné to, že uvedené 2 podmínky se vztahují k nejbližšímu následujícímu pravidlu a je mezi nimi spojka „AND“. Druhé pravidlo nemá podmínku žádnou. Pokud se vykoná první pravidlo, druhé pravidlo již „nenamatchuje“, protože se dále pracuje s již pozměněnou URL.
# různé verze stránek podle prohlížeče RewriteCond %{HTTP_USER_AGENT} ^Lynx/.* RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[12].* RewriteRule ^foo\.html$ foo.20.html RewriteRule ^foo\.html$
foo.32.html
7
[OR] [L] [L]
mod_rewrite – Martin Bartušek, Petr Šťastný Podobný příklad pro podstrkávání různých verzí stránek na základě nějaké vlastnosti prohlížeče návštěvníka. V tomto případě máme k dispozici jednodušší verzi stránek pro starší prohlížeče. Podmínky zde mají mezi sebou spojku „OR“. Druhé pravidlo funguje jako výchozí, pokud nejsou podmínky pro první pravidlo splněny.
# zákaz vložení našich obrázků na cizí stránky # (Blocked Inline-Images) RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://www\.example\.com/.*$ [NC] RewriteRule ^.*\.gif$ [F]
Tento příklad pracuje s hlavičkou HTTP_REFERER, ve které prohlížeče při požadavku zasílají URL adresu stránky, ze které se přišlo nebo na kterou se požaduje nahrát nějaký objekt. V tomto případě chceme znemožnit, aby si mohli lidé vložit na cizí stránky obrázky s příponou gif přímo z našeho webu. Pokud se tedy obrázek má načíst na jiné než naše stránky, vrátí server odpověď Forbidden. Navíc však musíme ošetřit situaci, kdy je hlavička HTTP_REFERER prázdná (prohlížeč má zakázáno ji posílat nebo to neumí).
# s podstrčením jiného obrázku RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://www\.example\.com/.*$ [NC] RewriteRule ^.*\.gif$ /stop-stealing-images.gif [L]
Varianta předchozího příkladu. Zde nevrátíme odpověď Forbidden, ale podstrčíme jiný obrázek.
# virtuální subdomény # http://home.example.com/file.html # -> http://home.example.com/subdomain/home/file.html RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC] RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC] RewriteRule (.*) subdomain/%2/$1 [L]
Toto je jednoduchý způsob, jak mít u své domény subdomény s různým obsahem a nemuset kvůli tomu vytvářet další virtualhost v Apache. Zjistíme si z hlavičky HTTP_HOST požadovanou subdoménu a podle toho podstrčíme obsah z příslušného podadresáře. Pro upřesnění: $1 v pravidle odkazuje na obsah první závorky v patternu pravidla (v tomto případě celá adresa) a %2 odkazuje na druhou závorku poslední podmínky (zde název subdomény).
# jednoduché použití mod_proxy RewriteCond %{HTTP_HOST} !^nasa\.proxy\.example\.com$ RewriteRule (.*) http://www.nasa.gov/$1 [P]
Zde vidíme jednoduchou ukázku využití modulu proxy, kdy pro všechny požadavky na náš web http://nasa.proxy.example.com/ podstrkáváme obsah z jiného web serveru na adrese 8
mod_rewrite – Martin Bartušek, Petr Šťastný http://www.nasa.gov/. Příznak P vynutí podstrčení přes proxy – web server odešle HTTP požadavek na cílový server a uživateli vrátí takto získanou stránku. Mnohem zajímavější použití mod_proxy uvidíme v následující části.
7. RewriteMap Klauzule RewriteMap přináší do přepisovacích pravidel další rozměr. Můžeme si to představit jako asociativní mapu (překladovou tabulku) klíč -> hodnota, podle které pravidla přepisují URL. RewriteMap nelze použít v souboru .htaccess, je přístupný pouze v konfiguraci web serveru.
RewriteMap MapName MapType:MapSource MapName – libovolné pojmenování mapy, pomocí tohoto názvu se na mapu odkazujeme v pravidlech či podmínkách. MapType – typ mapy: ●
txt – Plaintext soubor, 1 záznam na řádek (klíč hodnota), oddělovač mezera, možné komentáře (#).
●
rnd – Plaintext soubor s možností uvedení více hodnot pro jeden klíč, hodnota je na základě klíče vybrána náhodně ze všech možných.
●
dbm – Hašovací tabulka pro rychlejší vyhledávání podle klíče. Data této tabulky pochází z textového souboru, který se poté překompiluje do formátu dbm.
●
int – Interní funkce, např. toupper, tolower, …
●
prg – Možnost volat externí program pro provádění překladů. Tento program je spuštěn při startu webového serveru, požadavky na překlad mu jsou posílány na jeho standardní vstup, hodnoty vrací na svůj standardní výstup. Program tedy není spouštěn znovu vždy při každém požadavku, to by bylo příliš náročné, zejména na zatížených web serverech.
MapSource – zdroj mapy (cesta k souboru, programu, název interní funkce) Na následujícím příkladu si předvedeme, co je možné pomocí přepisovacích map udělat. Představte si situaci, kdy máte několik webových serverů, na nichž běží stejný web, a chceme mezi ně rozdělit zátěž. Máme 4 web servery pro dynamický obsah, 2 webservery pro statický obsah (obrázky) a jeden web server, který je před nimi – přichází na něj požadavky a ty jsou pak rozdělovány na ostatní servery. Mějme následující textový soubor s mapou a k němu pravidla: # soubor map.txt static dynamic
www1|www2|www3|www4 www5|www6
# komentář
9
mod_rewrite – Martin Bartušek, Petr Šťastný # soubor httpd.conf # (RewriteMap nelze použít v .htaccess) # load balancing s mod_proxy RewriteMap svr rnd:/path/to/file/map.txt RewriteRule ^(.*\.(png|gif|jpg)) http://${svr:static}/$1 [NC,P,L] RewriteRule ^(.*) http://${svr:dynamic}/$1 [P,L]
Teď si podrobně rozeberme pravidla. Nejprve si nadefinujeme mapu, tedy načteme textový soubor a tuto mapu si pojmenujeme „svr“, abychom se pak na ni mohli odkazovat. První pravidlo se stará o statický obsah, v tomto případě o obrázky s příponou png, gif a jpg. Takové HTTP požadavky jsou přesměrovány na jeden ze serverů www5 nebo www6. Nejedná se však o přesměrování, ale obsah je přes mod_proxy z cílového serveru načten a pak přeposlán uživateli, ten o tom však neví a myslí si, že data pocházejí z tohoto web serveru. Druhé pravidlo se aplikuje v ostatních situacích, ve kterých se o zpracování požadavku postará některý ze serverů www1 až www4, opět přes proxy modul. Volba hodnoty u jednoho klíče probíhá mechanismem round-robin (tedy první, druhý, … , poslední a pak zase od začátku), ale nikdy nemůžeme předvídat, na jakou hodnotu zrovna přijde řada. Kouzlo souborů s mapou spočívá v tom, že není podstatné, kde se bere jejich obsah. Jakákoliv aplikace či skript může tyto textové soubory aktualizovat za běhu, můžeme tedy generovat mapu hezkých URL stránek z databáze, žádné meze se nekladou. Pomocí externího programu si můžeme s mapou dělat úplně cokoliv.
10