Subdomény pomocí.htaccess
23. dubna 2011
Vytvářejte na svém hostingu subdomény zcela bezproblémově, dynamicky, bez nutnosti cokoli konfigurovat, jen vytvořením odpovídajícího adresáře.
(Aktualizováno 2013–07–02)
Ze
začátku si ujasněme co by měl náš kód umět:
- vytvářet
dynamické subdomény tak, že jenom vytvoříme adresář
v
DOCUMENT_ROOT
- přesměrovávat adresu
example.com
nawww.example.com
- přesměrovávat adresu
www.subdomain.example.com
nasubdomain.example.com
- zareagovat na neexistenci subdomény
Dále si specifikujme adresářovou strukturu.
Subdomény budeme tvořit vytvořením adresáře se jménem, které odpovídá
názvu domény. Náš kód požaduje u jména ještě příponu
_root
. Takže naše adresářová struktura bude pro subdomény
forum
, irc
a hlavní doménu vypadat
následovně:
- DocumentRoot
forum_root
(doménaforum.example.com
)irc_root
(doménairc.example.com
)www_root
(obsah pro hlavní doménuwww.example.com
)
Tak a začneme
zlehka – zapnutím mod_rewrite
:
RewriteEngine on
Je také
dobré zakázat výpis souborů v adresáři pomocí
Options -Indexes
a pro správnou funkčnost
mod_rewrite
je vhodné zapnout volbu
FollowSymLinks
:
Options -Indexes
Options +FollowSymLinks
Tak
a nyní se pustíme do přesměrovaní. Nejprve přesměrování domény
www.subdomain.example.com
na doménu
subdomain.example.com
:
# presmerovani subdomeny s www na verzi bez www
RewriteCond %{HTTP_HOST} www\.(.*)\.([^\.]+)\.([^\.]*)$
RewriteRule (.*)$ http://%1.%2.%3/$1 [R=301,L,NE]
Pokračovat
budeme přesměrováním domény example.com
na variantu
www.example.com
:
# presmerovani domeny 2. radu na verzi s www
RewriteCond %{HTTP_HOST} ^([^\.]+)\.([^\.]*)$
RewriteRule (.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L,NE]
Tak a nyní konečně podstrčení, které nám zobrazí obsah požadované subdomény:
# podstrceni obsahu
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteRule ^(.*)$ %1_root/$1 [L]
Pokud si teď kód v této podobě vyzkoušíte, zjistíte, že způsobuje vnitřní chybu serveru (tj. chybu „500 Internal Server Error“). Příčinou je zacyklení při podstrčení. Proto před samotné podstrčení přidáme kód, který zjistí zda požadavek pochází od klienta či vzniknul přepisem adresy.
# ochrana proti zacyklení
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# podstrceni obsahu
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteRule ^(.*)$ %1_root/$1 [L]
Kód by měl být v současné podobě funkční a neměl by způsobovat zacyklení. Pro někoho možná dostačující, my ale půjdeme ještě dál. Pokud si totiž s kódem začnete hrát zjistíte, že má několik nedostatků.
Zaprvé jsme chtěli, aby kód nějakým způsobem zareagoval
na neexistenci subdomény, zadruhé zjistíte, že náš kód je v menším
konfliktu s modulem mod_dir
. Dochází k tomu v případě, že
se pokusíme přistoupit k adresáři a na konci adresy neuvedeme lomítko.
V tomto případě mod_dir
automaticky provede přesměrování na
verzi s lomítkem. Výsledná adresa je z pohledu serveru správná (např.
http://example.com/forum_root/nejaka-slozka/
), ale pro nás
bohužel nikoli. Proto pomocí direktivy DirectorySlash
vypneme
doplňování lomítka.
# bude uvedeno uplne na zacatku
DirectorySlash Off
Jelikož ale nechceme o funkci doplnění lomítka přijít, přidáme k našemu kódu následující obsluhu:
# presmerovani na verzi s lomitkem
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteCond %{DOCUMENT_ROOT}/%1_root/%{REQUEST_URI}/ -d
RewriteRule ^(.*)[^/]$ %{REQUEST_URI}/ [R=301,L,NE]
Obsluhu vložíme za „ochranu proti zacyklení“. Následně ještě přidáme kontrolu existence subdomény, pokud subdoména neexistuje odešleme uživateli HTTP kód „410 Gone“:
#kontrola existence subdomeny
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteCond %{DOCUMENT_ROOT}/%1_root !-d
RewriteRule ^ - [G,NC,L]
Kód
vložíme za „ochranu proti zacyklení“ a před „přesměrování na verzi
s lomítkem“. Teď už jenom zbývá náš kód zkompletovat. K tomu ještě
přidáme podmíněné bloky, které nám zkontrolují přítomnost
jednotlivých modulů (mod_rewrite
a
mod_dir
).
Kompletní kód
<IfModule mod_dir.c>
DirectorySlash Off
</IfModule>
Options -Indexes
Options +FollowSymLinks
<IfModule mod_rewrite.c>
RewriteEngine on
# presmerovani subdomeny s www na verzi bez www
RewriteCond %{HTTP_HOST} www\.(.*)\.([^\.]+)\.([^\.]*)$
RewriteRule (.*)$ http://%1.%2.%3/$1 [R=301,L,NE]
# presmerovani domeny 2. radu na verzi s www
RewriteCond %{HTTP_HOST} ^([^\.]+)\.([^\.]*)$
RewriteRule (.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L,NE]
# ochrana proti zacyklení
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
#kontrola existence subdomeny
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteCond %{DOCUMENT_ROOT}/%1_root !-d
RewriteRule ^ - [G,NC,L]
# presmerovani na verzi s lomitkem
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteCond %{DOCUMENT_ROOT}/%1_root/%{REQUEST_URI}/ -d
RewriteRule ^(.*)[^/]$ %{REQUEST_URI}/ [R=301,L,NE]
# podstrceni obsahu
RewriteCond %{HTTP_HOST} ^(.*)\.([^\.]*)\.([^\.]*)$
RewriteRule ^(.*)$ %1_root/$1 [L]
</IfModule>
Kód bez problémů používám na WEDOSu, kde tímto kódem nahrazuji podle mě nepraktickou výchozí obsluhu subdomén, a Endoře. Na jiných hostinzích jsem zatím neměl možnost kód otestovat.
Poznámka k mod_rewrite (aktualizováno 2012–09–19)
Pokud budete pomocí tohoto kódu realizovat subdomény a
na nějaké subdoméně budete chtít používat další přepisování URL
(třeba pro použití tzv. hezkých URL), narazíte na problém, že se pravidla
mod_rewrite
nedědí do podadresářů, tj. zde uvedený kód se
vůbec nespustí. Řešením je použítí
RewriteOptions
. InheritBefore inheritBohužel toto
je možné až od Apache 2.3.10. Pokud tedy nemáte na serveru
Apache 2.3.10 nebo vyšší, budete muset do souboru.htaccess před vaše
přepisování zkopírovat kód pro subdomény:
#.htaccess na subdomene (napr. www_root)
RewriteEngine on
RewriteOptions inherit
# kontrola zda se provedly prikazy v.htaccess pro subdomeny
RewriteCond %{ENV:REDIRECT_STATUS} 200
# presmerovani vsech pozadavku na index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz)$ index.php [L]
# soubor.htaccess na subdoméně, tj. např. ve složce /forum_root
# zde bude kód pro subdomény
#...
# zde budou ostatní pravidla mod_rewrite, třeba přesměrování
# všech požadavků na soubor index.php:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz)$ index.php [L]
Poznámka k direktivě Options
V kódu je použit následující kód:
Options -Indexes
Options +FollowSymLinks
Ujistěte
se, že je na vašem serveru direktiva Options
povolená. Na většině serverů (včetně freehostingů) se
tímto nemusíte zabývat, ale třeba u webhostingu Forpsi jsem se setkal
s tím, že byla tato direktiva zakázaná a pak docházelo k chybě „500
Internal Server Error“.
Aktualizace 2012–08–31
Aktuální
verzi kódu pro dynamické subdomény pomocí .htaccess
, lze nyní
najít na GitHub Gist – konkrétně zde. V kódu došlo k několika
změnám:
- při neexistenci subdomény je nyní odesílán stavový
kód
404 Not Found
namísto kódu410 Gone
. - v kódu přibyla ukázka toho, jak si nadefinovat vlastní chybovou stránku (ukázku je nutné odkomentovat a případně – zcela určitě – upravit).
V tomto článku jsem žádné změny nedělal, článek tedy obsahuje původní kód. Kdo chce novou verzi ať navštíví GitHub Gist :).
Aktualizace 2013–07–02
Zdrojový kód přesunut z Gistu do repozitáře na GitHubu.