pokročilá manipulace s URL od základů
Na serveru Apache může být nainstalován modul mod_rewrite, který umožňuje podnikat psí kusy s URL adresami -- přesměrování a podstrkávání. Mod_rewrite je dost šikovná věcička na složitější problémy. Na druhou stranu je dost těžké se s ním naučit, mně to trvalo asi rok a stejně to ještě pořádně neumím. Tento návod by vám měl ušetřit počáteční tápání (a ten rok času).
Většina přesměrování se dá udělat i jednodušeji (například meta tagem nebo pomocí mod_alias).
Dá se mod_rewrite použít? - První příklady na Mod_rewrite - Obecný zápis RewriteRule - Podstrkávání a přesměrování - Příklad jednoduchého regulárního výrazu - Proměnné z regulárů - Podmínky RewriteCond - Příznaky v hranatých závorkách - Varianty k mod_rewrite - Bacha na interpretaci cest - Končím tam, kde jiní začínají
Ne na každém serveru se dá pracovat s mod_rewrite. Požadavky nutné:
I tak to pořád nejsou podmínky dostačující. Někdy se mohou direktivy mod_rewrite třískat s jinými existujícími direktivami. Na mém serveru například začal mod_rewrite fungovat až poté, co jsem si do .htaccess přidal zápis
Options +FollowSymlinks
Jestliže nevíte, jestli mod_rewrite a .htaccess fungují, tak to prostě zkuste, případně se zeptejte správce serveru.
Jestliže jste nikdy nezkoušeli konfigurovat server nebo pracovat se souborem .htaccess, mod_rewrite asi není dobrá parketa, na které se to učit. Zkuste si napřed jednodušší věci s .htaccess.
Asi jsem v životě v neviděl více serverových chyb, než když jsem se učil pracovat s mod_rewrite. Když se to rozbije, tak server hází chybu serveru s číslem 500 často i u stránek, se kterými jsem si původně hrát nechtěl. Užijete si spoustu srandy, stojí to za to.
Horší je, když nějakým pokusem mod_rewrite zacyklím. Pak se pětistovka objevit nemusí -- já koukám na prázdnou stránku a v serveru se zatím peče procesor.
Osobně jsem věci s mod_rewrite vždy testoval v podadresáři na svém domácím serveru. Dělejte to taky tak, jestli můžete (zkuste třeba EasyPHP). Jestli nemůžete, poraďte si jinak, ale vážně nedoporučuji experimentovat na ostrém serveru v adresáři, kde máte živé stránky. Přinejhorším si tam udělejte testovací adresář s vlastním .htaccess a hrajte si v něm.
Pokud pracujete s http.conf, nezapomeňte po každé změně server restartovat.
Níže uvedená zaklínadla se zapisují do souboru .htaccess nebo httd.conf.
Budu chtít, aby se mi stránka http://muj-server.cz/pozadovany-soubor.html
přesměrovala na http://muj-server.cz/vysledny-soubor.html
. Do souboru
.htaccess nebo httpd.conf zadám tento kód:
# presmerovani
RewriteEngine on
RewriteRule pozadovany-soubor\.html
/vysledny-soubor.html
[R]
Když pak čtenář požádá o pozadovany-soubor.html, tak místo něj dostane vysledny-soubor.html.
Tuto novou adresu vysledny.soubor.html uvidí i v adrese prohlížeče. To [R] je jako redirect, tedy přesměrování (hodí ho to na jinou adresu, než chtěl).
Cíl přesměrování musí být absolutní adresa. Všimněte si, že /vysledny.soubor.html začíná lomítkem, a tak se počítá od rootu webu (a je to tedy absolutní adresa).
Do souboru .htaccess nebo httpd.conf se zadá tento kód:
# podstrceni
RewriteEngine on
RewriteRule zadana-stranka\.html
podstrcena-stranka.html
Místo zadané stránky se pak objeví obsah podstrčené stránky. Zadaná adresa v prohlížeči ale zůstává stejná, totiž zadana-stranka.html. Proto tomu říkám podstrčení, odborněji se tomu říká přepisování. (Adresa bude červená, obsah zelený.)
Bude se to podstrkávat, protože tentokrát tam není to [R].
Protože zápisy adres v tomto příkladu nezačínají lomítkem, adresa zadané stránky se odvozuje od adresáře, ve kterém se vyskytuje .htaccess. Stejně tak podstrcena-stranka.html (ta musela být v minulém příkladu zadávána absolutně s lomítkem na začátku). Kdyby podstrčená stránka začínala lomítkem, počítala by se nikoliv od aktuálního adresáře, ale od kořene webu.
První parametr se chápe jako regulární výraz. V zadané stránce (první parametr) se tedy musejí některým znakům předřazovat zpětná lomítka (typicky třeba před tečku), aby se tyto znaky jako regulární výraz neinterpretovaly. Proto je zadaná stránka zapsaná se zpětným lomítkem před tečkou: zadana-stranka\.html. Naopak v cílové stránce se žádné znaky nanahrazují (druhý parametr není regulár).
Zápis mod_rewrite vždy začíná jeho zapnutím:
RewriteEngine on
Je potřeba mít to tam samozřejmě vždycky, ale v příkladech to někdy vynechávám.
RewriteRule má následující syntaxi:
RewriteEngine on
RewriteRule Na-co-se-ptá-klient
Co-skutečně-dostane
[nepovinná-pravidla]
První řádek RewriteEngine on
je to zaklínadlo, kterým se mod_rewrite
zapíná.
RewriteRule: Na co se ptá klient (první parametr), se zpracovává jako cesta k souboru odvozená od rootu webu. Příklady: /index.html, /archiv/akce.php Je to regulární výraz (některé znaky se musejí escapovat). Když server vidí, že klient chce stránku, která odpovídá prvnímu parametru, začne něco podnikat.
Adresa stránky, kterou uživatel skutečně dostane (druhý parametr), je buďto absolutní adresa (začíná na http://), nebo relativní. Relativní adresa se odvozuje buďto od aktuálního adresáře, nebo od rootu webu -- to pokud zápis Co-skutečně-dostane začíná lomítkem. Například http://priklad.cz/soubor.html je adresa absolutní, zápis /soubor.html je adresa relativní. (Pokud máte s relativními adresami potíž, můžete mít divně nastavený server. Najděte si, jak funguje RewriteBase.)
Tento druhý parametr se narozdíl od prvního nezapisuje jako regulární výraz, například se tedy nemusejí escapovat tečky zpětným lomítkem (ale zase když se zaescapují, tak to nevadí).
Co se týká nepovinných pravidel, budu je různě zmiňovat v dalších příkladech a na na konci tohoto textu.
Pro pochopení mod_rewrite je naprosto nutné, abyste perfektně porozuměli rozdílu mezi podstrkáváním a přesměrováním. Možná se vám bude zdát, že se opakuji, ale je to důležité.
Proberu opět jeden z původních příkladů. (Server bude všechny soubory chápat jako by byly v aktuálním adresáři.)
RewriteEngine on
RewriteRule zadana-stranka\.html
podstrcena-stranka.html
V tomto příkladu se neprovede přesměrování, nýbrž podstrčení (není tam to [R]). To znamená, že uživatel stále uvidí adresu, kterou zadal (případně na kterou kliknul). Server mu na tuto adresu "podstrčí" obsah souboru podstrcena-stranka.html. Všimněte si, že tentokrát nejsou v žádných hranatých závorkách žádné kódy -- podstrkávání je výchozí chování mod_rewrite.
Když budu mít soubor .htaccess s příkazem na podstrkávání například v adresáři adresar, tak se mi může stát, že
HTTP hlavičky (a vůbec všechno) vypadají zvenku tak, jako by soubor /adresar/zadana-stranka.html skutečně existoval. Například http kód odpovědi je "200 OK". Přitom ten soubor zadana-stranka.html ve skutečnosti vůbec nemusí existovat (a většinou se taková věc dělá právě proto, že neexistuje).
Když v předchozím příkladu přidám na konec řádku RewriteRule do hranatých závorek pravidlo [R=301], takto:
RewriteRule (.*) /vysledek.html [R=301]
tak se podstrčení změní na přesměrování.
Čili v tuto chvíli jde o přesměrování, nikoliv o podstrčení. Kdyby tam bylo jenom [R] bez =301, tak to taky bude fungovat, ale server vrátí kód odpovědi 302 found (což je většinou to samé, co 301, akorát 302 se bere jako dočasné přesměrování, 301 jako trvalé).
Výchozí chování serveru je podstrkávání. V jakých případech probíhá přesměrování:
RewriteRule (.*) https://www.jakpsatweb.cz
Pokud potřebujete podstrkávat obsah z cizího serveru, používá se na to [P] a já tomu říkám proxování.
Jednou z krás mod_rewrite je to, že dovoluje ve svých direktivách využívat regulární výrazy.
Pravidlo, které všechno z aktuálního adresáře (ve kterém je .htaccess) přesměruje na hlavní stránku www.jakpsatweb.cz:
RewriteEngine on
RewriteRule (.*)
http://www.jakpsatweb.cz [R]
Ten zápis (.*) znamená "cokoliv" (přesněji řečeno libovoný počet (to je hvězdička) libovolných znaků (to je ta tečka)).
Pokud v tomto případě uživatel napíše libovolnou adresu, která míří do aktuálního adresáře (to je adresář, ve kterém mám .htaccess), přesměruje ho to na http://www.jakpsatweb.cz. Tím eRkem v závorce opět říkám, že se bude přesměrovávat.
Tento příklad není moc užitečný (přesněji řečeno není k ničemu), je ale relativně jednoduchý, proto jím začínám výklad.
Převádění jednoho souboru na druhý není moc užitečné. Mnohem lepší je v zavolaném url něco najít a použít to na volání něčeho jiného. To "něco" bude proměnná.
Třeba najdu číslo článku a použiju ho na volání skrytého url. Následující příklad předpokládá, že stránky jsou napsané v php a jejich adresy mají normálně za otazníkem parametry. Články mají třeba toto url:
nějakýweb.cz/skript.php?id=234
Já bych ale chtěl na tuto stránku odkazovat a zapisovat adresou bez otazníku, například
nějakýweb.cz/clanek-234
Udělám to tak, že v pravidle pro mod_rewrite najdu číslo článku jako proměnnou (bude se jmenovat $1) a tuto proměnnou použiju při definici toho, co se má podstrčit. Zápis pravidla vypadá tato:
RewriteRule ^clanek-(.*) skript.php?id=$1
Vysvětlení zápisu:
Předchozí příklad úpravy ošklivých dynamických adres (s otazníkem) na hezké adresy se hodí pouze v případě, že máte aplikaci, která už ty hezké adresy používá v odkazech. Pokud máte starou aplikaci, která na každém kroku odkazuje na skripty se spoustou parametrů, tak se to musí řešit mnohem složitěji. (Dvojitě s nějakým dummy parametrem. Podrobnosti v pokročilých příkladech.)
Samotný zápis pomocí regulárů umožňuje hodně kouzel. Když ale reguláry nestačí, nastupují další možnosti podmínkování, kdy se má manipulace provést, a kdy ne.
Požadavky na můj web mohou požadavky přicházet s hostname "jakpsatweb.cz" nebo "www.jakpsatweb.cz". Preferuji, aby lidé i vyhledávače chodili jenom na verzi s www, a tak je přesměrovávám.
Příklad: Všechny požadavky, co míří na rovnou.cz bez www, přesměruju na verzi domény s www:
RewriteEngine on
RewriteCond %{HTTP_HOST}
^rovnou\.cz [NC]
RewriteRule (.*) http://www.rovnou.cz/$1 [R=301,QSA,L]
S pravidlem RewriteRule se pracuje pouze v případě, že je splněna podmínka RewriteCond. V tomto případě jsem například testoval proměnnou %{HTTP_HOST}, ve které je hostname (doménová část) vyžádaného url. Pokud hostname ("rovnou.cz" nebo "www.rovnou.cz") začíná (to je ta stříška) hned řetězcem "jakpsatweb.cz", tak se pravidlo provede. To je to, co chci - přesměrovat na verzi s www.
A proto když někdo požádá o https://rovnou.cz/cokoliv, je přesměrován na https://www.rovnou.cz/cokoliv . Příznak QSA v hranatých závorkách ještě zařídí, že se do nové adresy přenese i query-string (část adresy za otazníkem). Pokud mi naopak vadí, že se parametry za otazníkem přenáší (v novějších verzích Apache od 2.4), použiju příznak [QSD].
Výše uvedený zápis musí být v rootu domény, protože jinak by se to $1 vyhodnocovalo relativně k adresáři (jméno adresáře by se uřízlo, nevím proč). Toto asi nějak souvisí s RewriteBase (nevím).
To [NC] v RewriteCond znamená, že nezáleží na velikosti znaků. Tu normálně server rozlišuje, což v tuto chvíli nechci.
RewriteEngine on
RewriteCond %{HTTP_HOST}
^www.example\.com [NC]
RewriteRule (.*) http://example.com/$1 [R=301,QSA]
Přepište example.com svou doménou. Dá se to napsat i obecněji, takže není nutno zadávat doménu:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,QSA,L]
Tento kód mi poslal Jakub Kubíček a okomentoval ho takto:
Riadok s RewriteCond: Podmienka -> Nasledujúce pravidlo vykonaj, iba ak serverová premenná začína ako www. a pokračuje lubovoľným počtom ľubovoľných znakov.
Riadok s RewriteRule: Samotné prepisovacie pravidlo. Hrubo preložené znamená: Presmeruj akýkoľvek počet akýchkoľvek znakov na hostname bez www, vytiahnuté ako back reference z reguláru v podmienke (%1), pričom za lomítko pridaj všetko, čo bolo v pôvodnej adrese ($1), z reguláru v RewriteRule.
Použité vlajočky (znaky v hranatých zátvorkách): NC: (not case) neberie do úvahy veľkosť písmen, čiže medzi napr. AbCDe a abcde nie je nijaký rozdiel. R: (redirect kód) presmeruje a vráti zadaný kód. defaultne je to 302, 301 bude lepšie. QSA (query string append): do presmerovanej adresy pripojí za otáznik všetko, čo bolo v požadovanej adrese. L: (last rule) táto vlajočka tam musí byť, pokiaľ máš pod týmto rewrite kódom ešte nejaký ďaľší. Hovorí, že toto je posledné pravidlo, a pri vykonaní zabráni vykonaniu zvyšných pravidiel.
RewriteCond %{HTTP_HOST} ^stara-domena\.cz
[NC]
RewriteRule ^(.*)$ http://www.nova-domena.cz/$1
[R=301,QSA,L]
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond se symbolicky zapisuje:
RewriteCond testovaný-řetězec
regulár
# pravidla RewriteRule
V testovaném řetězci se dají používat proměnné, a v reguárech zvláštní znaky. Např v příkladu:
RewriteCond %{HTTP_HOST} ^jakpsatweb\.cz
%{HTTP_HOST} je hostname, tedy celé jméno domény. Stříška v příkladu znamená začátek řetězce. Zpětné lomítko před tečkou říká, že se hledá opravdová tečka (jinak tečka v regulářu znamená libovolný znak). Kdyby tam to zpětné lomítko nebylo, tak se v tomto případě nic nestane, protože v hostname na tomto místě těžko může být něco jiného než tečka. Proto ji v některých příkladech vynechávám.
Více podmínek se řetězí operátory [OR] a píšou se na více řádků. Například:
RewriteCond %{REMOTE_HOST} ^domena1.* [OR]
RewriteCond %{REMOTE_HOST} ^domena2.*
Pokud se za sebe napíše na více řádků více RewriteCond bez operátoru [OR], předpokládá se, že mají platit oba najednou (takže tam není nic jako [AND]).
%{REQUEST_FILENAME}, %{REQUEST_URI}, %{REQUEST_METHOD}, %{REMOTE_ADDR}, %{REMOTE_HOST}
%{PATH_INFO}
%{QUERY_STRING}, %{SERVER_PORT}, %{SERVER_NAME}, %{HTTPS} (s hodnotou on)
%{HTTP_USER_AGENT}, %{HTTP_COOKIE}, %{HTTP_ACCEPT_LANGUAGE}, %{HTTP_HOST}, %{HTTP_REFERER}
%{ENV:REDIRECT_STATUS}
%{DOCUMENT_ROOT}/ (používá se spíše v druhém parametru, který říká, kam to půjde)
a možná ještě další
V příkladech použití mod_rewrite, které porůznu najdete na webu, se na konci pravidel často používají příznaky v hranatých závorkách. Anglicky se jim říká flags. Znám a používám jich jenom pár:
Více příznaků v hranatých závorkách se v odděluje čárkou.
Na Windowsovském serveru IIS prý taky existuje něco, co funguje podobně jako mod_rewrite, ale nevím o tom nic víc než to, že to prý je nějaký ISAPI modul nazvaný URL Rewrite. Má mít vlastní formu zápisu pravidel podobnou XML.
Bezplatně by k IIS měl jít připojit Ionics Isapi Rewrite Filter, což má být ISAPI filtr. Umí pracovat s většinou standardně používaných pravidel pro Apache, včetně transparentní proxy (za info děkuji Ondřeji Kopeckému).
Pokud na linuxovém serveru nemůžete použít mod_rewrite (třeba na serveru nefunguje), v mnoha případech na přesměrování stejně dobře poslouží mod_alias. Jeho direktiva RedirectMatch mi pomohla v mnoha situacích. (Vizte dokumentaci mod_alias - anglicky.) Podobné věci prý umí mod_redirect, ale s ním nemám žádnou zkušenost.
Když prohlížeči, který požaduje stránku A, podstrčíte stránku B, která na svém původním místě používá relativní adresy (obrázky, styly, odkazy), tak ty adresy nebudou fungovat. Uvědomte si, že prohlížeč (obecně klient) odvozuje cesty od adresy, kterou vidí (a uživatel ji vidí ve stavovém řádku). Jestliže stránky A a B leží ve stejném adresáři (i virtuálním), tak je to v pohodě, odkazy budou fungovat. Jestli ale leží jinde, musíte v podstrčeném kódu používat absolutní adresy (alespoň musejí začínat lomítkem), nebo předem počítat s budoucím umístěním na adrese A (to se ale dost blbě ladí).
To se týká podstrkávání. Při přesměrování tenhle problém samozřejmě není.
Různých návodů na mod_rewrite se po webu potuluje spousta, věřím, že nějaký naleznete a pustíte se do dalšího studia. V tomto textu jsem popsal základy, které dělaly problém mně. Nic víc jsem tímto návodem nesledoval.
Vyšší dívčí příkladů na mod_rewrite sepsal Petr Neuman.
Jak psát web píše Yuhů, Dušan Janovský. Kontakt.