Приложна магия с mod_rewrite в htaccess

Публикувано на: 10.06.2009

Последна актуализация на:

Пренаписване на интернет адреси с .htaccess

Промяната на интернет адреси с mod_rewrite се прави основно по следните seo причини:

  • съкращаване на адресите: адресите стават за индексиране от търсачките (seo friendly) и лесни за ръчно изписване в браузър
  • пренасочване на стари адреси към нови при преименуване на файлове на страници
  • ако има страници, които се достъпват с повече от един адрес се пренасочват излишните адреси
  • скриване на вида файлове на страниците на сайта

Пренасочванията се извършват от правила зададени във файла .htaccess, който трябва да се намира в главната уеб директория.

Правилата използват регулярни изрази (regular expressions) и могат да бъдат много сложни. В тази статия обаче ще разгледам само най-често ползваните правила от SEO специалистите.

За да работи този файл е нужно уеб сървъра да има инсталиран модул mod_rewrite и в конфигурационния файл /etc/httpd/conf/httpd.conf  трябва да е зададена опцията AllowOverride All

На повечето съвременни хостинг сървъри тези опции са включени по подразбиране, ако това не е така ще трябва да се свържеш със системния администратор на хостинга.

Проверка дали mod_rewrite работи

Постави следните редове в .htaccess файла и опитай да отвориш адреса http://domain.com/test  където domain.com е името на твоя сайт. Ако се отвори сайта ganbox.com това значи всичко е наред и mod_rewrite работи.

Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteRule test https://ganbox.com/? [NC,R,L]

Обикновено файла .htaccess започва със следните няколко реда. За краткост ще бъдат пропускани в примерите долу.

Options +FollowSymLinks -Indexes -MultiViews RewriteEngine On RewriteBase /

Просто пренасочване на преименувана страница

Ако страница /about.html е преименувана на /contact.php

redirect 301 /about.html /contact.php

аналогично

RewriteRule ^about\.html$ /contact.php [R=permanent,L]

[L]  казва, че това е последното правило, което трябва да се обработи.

Скриване на вида файлове

Всеки адрес от вида domain.com/страница, като страница може да съдържа малки или главни букви, цифри и знаците "-" и "_".

RewriteRule ^([-\w0-9_]+)$ /$1.php [L,NC,QSA]

Пример: при написване на адрес domain.com/форма-за-контакти2 ще бъде потърсена страница /форма-за-контакти2.php

Внимание: ако уеб съръвра е на Windows машина, горния регулярен израз вероятно няма да сработи,заради бъг в win32 mod_rewrite апаче модула.

RewriteRule ^([-А-Яа-яA-Za-z0-9_]+)$ /$1.asp [L,NC,QSA]

Вместо \w или А-Яа-яA-Za-z  използвай АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюяA-Za-z

В този пример при написване на адрес domain.com/Условия_За_Ползване ще бъде потърсена страница /Условия_За_Ползване.asp

като в полето за адреси на браузъра ще остане да се вижда http://domain.com/Условия_За_Ползване

Очевидно е, че по този начин няма как да се разбере какви скриптове управляват страниците на сайта. Дори може да се направи страниците да изглеждат като html файлове, а да се изпълняват едноименни php файлове с правилото:

RewriteRule ^([-А-Яа-яA-Za-z0-9_]+)\.html$ /$1.php [QSA,L]

в този случай ако напишеш http://domain.com/clients.html ще бъде потърсен файла /clients.php. Този подход има двоен положителен ефект - от една страна търсачките и от друга сигурност.

Съкращаване на адреси

Много често при SEO оптимизация на динамични сайтове с CMS се налага съкращаване на адреси.

Дълги адреси като този

http://domain.com/index.php?sec=pro&cat=48&category=avto

да изглеждат като този

http://domain.com/sec/pro/cat/48/category/avto

RewriteRule ^sec/([^/]+)/cat/([0-9]+)/category/([^/]+)/?$ /index.php?sec=$1&cat=$2&category=$3 [QSA,L]

Разбира се горното правило се пише на един ред, като между отделните части има по един интервал.

След това в кода се правят промени на всички места, които образуват връзки, за да откриеш всички тези места може да търсиш във всички файлове за http или за href.

Хубавото е, че старите адреси продължават да работят и не е проблем за хората, които са направили Bookmark към стар адрес.

Пренасочване на адреси към един файл

Превод на адреси от вида www.domain.com/contacts към файл index.php, като подава по GET в променлива p името на страницата www.domain.com/index.php?p=contacts

# ако не бъде открит файл
RewriteCond %{SCRIPT_FILENAME} !-f
# ако не бъде открита директория
RewriteCond %{SCRIPT_FILENAME} !-d
# подава низа към файла index.php
RewriteRule ^([^/]+)/?$ /index.php?p=$1 [QSA,L]

По този начин всеки адрес ще се изпраща за обработка към един единствен файл index.php често наричан диспечер (dispatcher).

В този файл се взема подадения низ и се слага в променлива $page. След това се проверява и ако файла със страницата не съществува се преминава към страница с грешка 404.php

$page= isset($_GET['p']) ? $_GET['p'] : 'home'; if(!file_exists(ABS_PATH.$page.'.php')) $page = '404'; include_once(ABS_PATH.$page.'.php');

От съображения за сигурност е много важно преди този код да е дефинирана константата ABS_PATH като в нея има абсолютен път до главната уеб директория например:

define('ABS_PATH', '/home/account/www/');

където account е потребителско име за cpanel на хостинга.

Ако се очаква в адреса на страницата да има само латински букви и тирета, тогава в index.php преди реда с file_exists добави следния ред:

$page = preg_replace('|[^A-Za-z_-]|','',$page); # премахва всичко различно от буква и знаците _ и -

Премахване на проблем с дублирани адреси

Наскоро имах следния проблем при оптимизиране на динамичен сайт. В навигацията сайта има два бутона за превключване на езика с българското и английското знаме.

Ако текущия адрес е http://domain.com/page.php?id=7

то бутона с английското знаме сочи към http://domain.com/page.php?lang=en&id=7

а бутона с българското знаме към http://domain.com/page.php?lang=bg&id=7

От потребителска гледна точка е удобно защото при смяна на езика оставаш в текущата страница, но от гледна точка на търсачката всяка страница на български има по два адреса.

Дори началната страница има 3 адреса: http://domain.com,  http://domain.com/index.php  и  http://domain.com/index.php?lang=bg

Решение:

# премахване на всички низове index.* от интернет адреса.
# Заб. Изтрий тези две правила ако искаш да инсталираш форум или блог
RewriteCond %{THE_REQUEST} \ /(.+/)?index\.([^/]+)(\?.*)?\  [NC]
RewriteRule ^(.+/)?index\.([^/]+)$ /%1 [NC,R=301,L]
# Правила за премахване на параметър lang=bg като запазва всичко останало в адреса

RewriteCond %{QUERY_STRING} ^(([^&]*&)*)(lang=bg)($|&)(.*)
RewriteRule .* $0?%1%5 [N,E=REMOVED:true]
RewriteCond %{ENV:REMOVED} true
RewriteRule ^ %{REQUEST_URI} [L,R=301]

Внимание: Ако добавиш първите две правила за премахване на index от адресите и в последствие решиш да инсталираш форум, блог или някакъв CMS в поддиректория в същия сайт, се очаква да имаш проблеми и  инсталацията няма да може да се извърши.

Премахване на www.

Пренасочване на  www.domain.com/страница  към domain.com/страница

RewriteCond %{HTTP_HOST} ^www\.(domain\.com)$ [NC] RewriteRule ^(.*)$ http://%1%{REQUEST_URI} [R=301,L]

Други полезни трикове в .htaccess

Ограничаване на достъпа

по IP адрес

За да забраниш достъпа до сайта от IP адрес 207.246.65.22 в началото на файла напиши

# ban web proxy Deny from 207.246.65.22

За да забраниш цялата мрежа 64.4.0.0 - 64.4.63.255 на Microsoft и техния MSN Bot напиши

Deny from 64.4.0.0/18

За да забраниш няколко  мрежи в Киев от които в блога вали спам изпращан от различни IP адреси като: 95.133.140.7, 95.133.192.198 и др. напиши:

Deny from 91.124.0.0/16
Deny from 91.195.12.0/23
Deny from 94.178.0.0/15
Deny from 95.132.0.0/14

Има предвид, че така блокираш много хора от Киев. Прецени сам дали очакваш читатели от там.

по тип на търсения файл

Забрана за показване на файлове, които завършват на .txt

<Files ~ "\.txt$">
    Order allow,deny
    Deny from all
</Files>

по вид браузър

Ако имаш директория /old в която има стара версия на сайта, която поддържа архаичния браузър Internet Explorer 6, може да пренасочиш всички заявки от такъв браузър заедно с подадените параметри към тях.

# за браузър IE6 или по-стар пренасочва към /old RewriteCond %{HTTP_USER_AGENT} MSIE\ [1-6] RewriteRule !^old(/.*)?$ /old%{REQUEST_URI} [QSA,L]

ограничаване на ботове

Ако забележиш в логовете, че твоя сайт е налазен от ботове може да използваш следния код:

RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule ^.* - [F]

Това ще блокира всички ботове, които не изпращат USER_AGENT. Няма да се блокират ботовете на търсачките Google, Yahoo и др. Ако някой програмист иска да обхожда твоя сайт с бот, това няма да го спре, защото ще ти подава валиден USER_AGENT. Също добра идея е да не се блокира с Forbbiden, а да се пренасочи към специална страница за ботове.

забрана на достъп до сайт през прокси

Напиши всичките тези правила и ще ограничиш достъпа до сайта от огромно количество свободни уеб прокси сайтове. Много е полезно за сайтове с форуми, където често потребителите си правят втори профил през прокси, за да се представят за друг човек.

RewriteCond %{HTTP:VIA}                 !^$ [OR]

RewriteCond %{HTTP:FORWARDED}           !^$ [OR]

RewriteCond %{HTTP:USERAGENT_VIA}       !^$ [OR]

RewriteCond %{HTTP:X_FORWARDED_FOR}     !^$ [OR]

RewriteCond %{HTTP:PROXY_CONNECTION}    !^$ [OR]

RewriteCond %{HTTP:XPROXY_CONNECTION}   !^$ [OR]

RewriteCond %{HTTP:HTTP_PC_REMOTE_ADDR} !^$ [OR]

RewriteCond %{HTTP:XROXY_CONNECTION}    !^$ [OR]

RewriteCond %{HTTP:X-FORWARDED-FOR}     !^$ [OR]

RewriteCond %{HTTP:FORWARDED-FOR}       !^$ [OR]

RewriteCond %{HTTP:X-FORWARDED}         !^$ [OR]

RewriteCond %{HTTP:HTTP_CLIENT_IP}      !^$

RewriteRule ^(.*)$ - [F]

Сигурност

Много важно! Ако ползваш на твоя сайт едно от следните: картинки против автоматични регистрации (captcha),  smarty темплейти или имаш директория, в която могат да се качват снимки или други файлове. Тогава вероятно тези директории имат повече права за писане в тях. Възможно е хакер да се възползва от тази слабост и да качи в такава директория root shell скрипт, който му дава достъп до файлове, база данни и изпълнение на команди. Пример за такива директории в WordPress са тези в  wp-content/uploads. Много добра идея е в такива директории да поставиш htaccess подобен на следния:

<Files ~ "^.*\.(php|pl|log|bak)">
 Order allow,deny
 Deny from all
 Satisfy All
</Files>

Това забранява достъпа до php и pl файлове качени в тази директория. Допълни в първия ред разделени с | разширенията на всички типове файлове, които не се очаква да присъстват в тази директория. Друг вариант е, ако директорията е само за картинки да изброиш типовете файлове, които се очакват да са в директорията със следния htaccess файл:

RewriteCond %{REQUEST_URI} !(\.(gif|jpe?g|png)$)
RewriteRule .* - [F,L]

Копирай този файл във всички директории за картинки. Сложи този файл в директорията upload на WordPress и той ще защити всички поддиректории, които се създават. В зависимост какви файлове качваш в блога, може да разшириш списъка. При мен е  !(\.(gif|jpe?g|png|zip|doc|xls|xml|html|txt)$) разбира се НЕ трябва да добавяш php иначе се губи смисъла.

Най-добрия вариант е ако може всички подобни директории да бъдат едно ниво нагоре извън главната уеб директория. Така няма да има нужда от htaccess в тях, защото дори хакер да качи файл няма да може да го достъпи през уеб. И освен това всеки път като архивираш проекта на сайта няма да копираш мегабайти излишна временна информация.

Обработка на грешки

Задаване на различни обработчици на възникнали грешки: служебна страница със съобщение , локална страница или файл на друг сървър.

ErrorDocument 403 "Sorry! Access denied!"
ErrorDocument 404 /404.html
ErrorDocument 500 http://drug.domain.com/error

в този пример, ако бъде написан адрес към несъществуваща страница, ще бъде зареден файла /404.html

Продължава в статията Пренасочване на домейн.

© В статията са ползвани материали от forum.modrewrite.com
Прочетена:33555
1 - лоша2 - слаба3 - средна4 - добра5 - отлична (2 гласа, оценка: 5,00 от 5. Моля изберете оценка!)
Loading...
Георги Стефанов
Георги Стефанов
Магистър по информатика, който се занимава с компютри от 1988 г., професионално с програмиране от 1998 г., а с уеб технологии от 2002 г. Има богат опит при оптимизиране на бизнес сайтове. Повече от 12 години развива успешно свои уеб проекти и работи с български и международни компании за постоянно подобряване на техните сайтове и увеличаване на онлайн продажбите. 

16 comments on “Приложна магия с mod_rewrite в htaccess”

  1. Просто няма какво да кажа. Страшно полезен материал. Особено това със забраната на проксита ще ми е много полезно. Благодарности!

  2. И не само това всичко тук е уникално полезно.
    Благодарности за статията!

  3. Поздравления!
    Много полезна статия.
    Материалът върши работа за всичко, не само за добро SEO.

  4. Описаното тук:
    "Премахване на www.
    Пренасочване на www. domain .com/страница към domain.com/страница

    RewriteCond %{HTTP_HOST} ^www\.(domain\.com)$ [NC]
    RewriteRule ^(.*)$ http://%1%{REQUEST_URI} [R=301,L]"
    

    стана точо обратно. Някаква идея къде е грешката?

    Друго интересно явление, че когато сложа .htaccess файла в главната директория (ползвам няколко домейна с един) всичко е ок. Но сложа ли го в директорията на конкретния сайт и става мазало (internal error). Някакви идеи?

  5. Options +FollowSymLinks -Indexes -MultiViews
    RewriteEngine On
    RewriteBase /
    RewriteRule ^([-А-Яа-яA-Za-z0-9_]+)$ /$1.html [L,NC,QSA]
    

    Изписвам този текст в един .htaccess и следователно очаквам след като изпиша примерно: domain .com/za_nas.html - очаквам в полето за адреси да се вижда само domain .com/za_nas - но уви, къде бъркам, Благодаря

  6. Екстра статия! Това за www към non-www го търся от 3 дни. Благодарности!

  7. здравей. опитвам се да си съкратя адресите, но не се получава. това е кода, който успях да напиша - RewriteRule ^products/([0-9]+)/session([a-zA-Z0-9_-]+)?$ /product_info.php?products_id=$1&osCsid=$2 [QSA,L]
    бих ли могъл да получа помощ защо не се получава ?

  8. Здравей, gan,
    Всички примери проработиха с изключение на този за съкръщаване на адресите. В твоя пример заместват ли се sec, cat, category със съответния GET. "След това в кода се правят промени на всички места, които образуват връзки" - в кой код, този в хтаксеса или този на сайта. Моля за помощ.

  9. Търся малко помощ с "приложната магия" с mod_rewrite. Супер много се заплетах и мисля че съм объркал нещо...

    Искам да направя url на кирилица: т. е.
    /kontakti.php --> /контакти
    Успях. С това парче код в .htaccess:
    RewriteRule ^контакти kontakti.php [L,QSA]
    RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /kontakti\.php\ HTTP/
    RewriteRule ^kontakti\.php$ http:// mgelektronika.com/контакти [R=301,L]
    последния ред: пренасочва /kontakti.php да отиде към /контакти

    Смених навсякъде линковете на кирилица - всичко е перфектно и работи:
    http:// mgelektronika.com/контакти

    Проблема е, че в Google при търсене, адреса пак излиза на латиница (в SERP резултатите). Също така и забелязах, че като проверя .htaccess кода през валидатор, пищи за грешки.
    В началото пуснах сайта с адреси на латиница, от 1 седмица са на кирилица, въпроса е аз ли греша нещо, или все още Google ги индексира с латинските адреси..?

  10. Необходимо е време, за да мине бота и да открие пренасочването. Това време зависи от това, колко често се променя сайта и може да бъде от часове до седмици. Може да ускориш процеса, като за всеки стар URL адрес направиш ping до търсачката, но аз препоръчвам, да оставиш бота сам да открие промяната. Относно втория въпрос, не знам с какъв инструмент валидираш htaccess файла, но предполагам, че е фалшиво съобщение за грешка породено от кирилицата.

  11. Здравей,
    Тепърва започвам да се занимавам с mod_rewrite и цяла вечер търся решението на един проблем и не мога да го намеря (търся в Гугъл), ще се радвам, ако може да ми помогнеш с решение или линк или каквото и да е. 🙂
    Проблемът е следният:
    Искам да предотвратя опасността от каквато и да е манипулация от страна на потребителя върху QUERY_STRING частта на URL адреса, но все пак да мога да парсвам GET променливите. Давам пример:
    Потребителят решава да въведе
    http:// site.ext/?lang=bg
    с това въвеждане потребителят не трябва да може да сменя езика, но с
    http:// site.ext/bg/, където правилото е
    RewriteRule ^(bg|en)/ ?lang=%1 [L]
    езикът трябва да бъде сменен.
    Пробвах с детектиране на HTTP_REFERER, но явно е било безуспешно.
    Благодаря предварително за съдействието!
    Лека вечер!
    Поздрави

  12. Здравейте, искам да попитам следното:
    качих на споделен хостинг един динамичен сайт за видео ( правен на php), на който адсенс рекламата не се показва само когато се зареди страница с дадено видео. А когато го сложа целия проект в отделна поддиректория, със абсолютно същите файлове и база данни работи перфектно дори и на страницата с видеото. Затова се принудих да редиректна сайта към поддиректорията. Т.е. в момента имам едно и също съдържание на domain.com и domain.com/papka и съм задал за ботовете noindex, nofollow временно.
    Може ли с “приложната магия” с mod_rewrite да направя така, че domain.com/papka да се превърне в domain.com виртуално, като използва само файловете в domain.com/papka
    Дано да съм успял да го обясня правилно.
    Благодаря предварително.

  13. Да, сравнително лесно, но малко зависи какви са настройките в момента. В главната уеб директория сложи .htaccess, в който след реда
    RewriteEngine on
    да има:
    RewriteRule ^$ subdir/ [L]
    RewriteRule (.*) subdir/$1 [L]
    като subdir е името на директорията, която съдържа файловете.
    Ако това не сработи опитай на мястото на последните два реда с тези редове:

    RewriteCond %{HTTP_HOST} ^(www.)?example.com$
    RewriteCond %{REQUEST_URI} !^/subdir/
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /subdir/$1
    RewriteCond %{HTTP_HOST} ^(www.)?example.com$
    RewriteRule ^(/)?$ subdir/index.html [L]

    където example.com е твоя домейн. Така тази директория subdir ще изглежда все едно е главна уеб директория.
    Може обаче да се наложи следното. Навсякъде, където зареждаш картинка или CSS така:
    <link rel="stylesheet" href="style.css" type="text/css" media="all" />
    или пък така:
    <link rel="stylesheet" href="http://example.com/style.css" type="text/css" media="all" />
    да трябва да се промени на:
    <link rel="stylesheet" href="http://example.com/subdir/style.css" type="text/css" media="all" />

споделиха
phone linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram