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 na www.example.com
  • přesměrovávat adresu www.subdomain.example.com na subdomain.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éna forum.example.com)
    • irc_root (doména irc.example.com)
    • www_root (obsah pro hlavní doménu www.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 inherit. Bohuž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ódu 410 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.

Líbí se vám tato stránka? Podpořte autora.
Become a Patron!