Настройка сайта на web-сервере с помощью .htaccess, показанная на реальном примере
Новиков М.Г.
28.04.2015
Часть 1. Общие понятия об инструментах настройки
Содержание:
Вступление
При переходе от статической технологии построения сайта к динамическому созданию страниц на стороне сервера (например, при использовании языка php) зачастую возникает необходимость доступа к настройкам самого сервера, под управлением которого работает сайт. Это может понадобиться, например, для реализации т.н. ЧПУ (Человеку Понятных Урлов), когда вместо трудночитаемой строки с параметрами после знака вопроса, определяющими для php страницу для формирования, пользователь оперирует обычными адресами к страницам привычного вида, которые преобразуются в нужный мудрёный вид уже самим сервером благодаря этим настройкам.
Например, фактическая ссылка на динамическую страницу, выполненную по технологии php, выглядит примерно так:
mysite.ru/index.php?page=articles/histoty/papyruses.php
Не фатально, но и не совсем понятно для рядового пользователя. Ссылка означает: «Велеть странице index.php, расположенной на сайте mysite.ru, отобразить в части себя страницу articles/histoty/computers.php». Для статического сайта ссылка на страницу выглядела бы более привычно и намного более читаемо:
mysite.ru/articles/histoty/computers.html
Задача очевидна — сделать так, чтобы пользователь вместо первой ссылки мог бы набирать вторую. Чтобы привести ссылку в порядок (сделать т.н. ЧПУ, то есть Человеку Понятный Урл), из неё надо убрать фрагмент «index.php?page=» и, желательно, поменять расширение с «.php» на привычное «.html», но при этом заставить сервер всё равно из этой удобной для человека ссылки получать именно первую, фактическую ссылку, чтобы скрипт на php мог нормально отработать, и сформировать то, что нужно. Всё это можно сделать, написав соответствующий скрипт для сервера, который перед отображением сайта превращал бы вторую ссылку в первую.
Кроме реализации ЧПУ, настройка сервера позволяет осуществлять также т.н. «склейку» адресов для поисковиков, осуществляя переадресацию с «лишнего» варианта адреса на основной, выдавая на него соответствующее сообщение (например, вариант адреса с поддоменом www склеить с тем же адресом без www). Если этого не сделать, поисковик может зафиксировать задублированный контент, и снизить ранг обоих адресов в поисковой выдаче. Замечу, что осуществить склейку можно и через аккаунты в самих поисковиках, если таковые у вас есть. Но этот способ менее универсален и требует много дополнительных телодвижений.
Ещё одним поводом для пользовательской настройки сервера может служить желание сделать собственные страницы сообщений об ошибках вместо безликих страниц типа страницы «ошибка 404».
Файл настроек .htaccess
Для реализации всех вышеупомянутых пользовательских настроек сервера Apache предназначен файл .htaccess, размещённый в общем случае в корне сайта. Сервер Apache использует этот файл следующим образом. Как известно, сервер занимается приёмом пользовательских запросов, их обработкой и отсылкой на них пользователю соответствующих веб-страниц. К серверу по умолчанию подключены различные модули расширения его функционала по обработке запросов, одним из которых является модуль mod_rewrite. Он помогает модифицировать строки запроса согласно определённым правилам перед тем, как они попадут на сам сервер для их обработки.
Правила для mod_rewrite записываются в виде скрипта прямо в .htaccess, так что с их размещением ничего сложного нет. Сложности наступают позже, при написании скрипта. Дело в том, что язык правил не очень эргономичен, и зачастую веб-мастера его элементарно не понимают. Интернет полон копипастами фрагментов скриптов, выполняющих то или иное действие, но мало где даётся подробная расшифровка того, что они делают. Одно из приятных исключений, статья:
в которой как раз-таки очень доступно описывается логика работы команд скрипта. Другим полезным источником может служить официальная документация по Apache:
Так что я настоятельно рекомендую изучить эти источники.
Скрипт, записанный в .htaccess, начинает выполняться сразу, как только на сервер поступает строка пользовательского запроса страницы. Но логика использования этого скрипта сервером такова, что делает написание такового довольно непростым делом. Основная сложность заключается в том, что при каждом запросе скрипт срабатывает, как минимум, дважды, и повторно прогоняет через себя уже готовую строку.
Это происходит из-за того, что сервер действует следующим образом. Если после выполнения скрипта запрос скриптом не изменился, сервер сразу отдаёт на него запрошенную страницу. Если же строка изменилась, что обычно и происходит при первом проходе, сервер снова переотправляет изменённую строку на свой вход, как будто она пришла извне, а это значит, что скрипт вынужден выполниться ещё раз.
Почему сервер так действует? Потому что после первого прохода скрипта запрос может указывать уже на другую директорию сайта, а в ней может находиться другой файл .htaccess, который в этом случае тоже должен выполниться. Файл .htaccess может лежать в каждой директории сайта, и действие его будет распространяться на текущую и все вложенные директории, пока во вложенной директории не встретиться другой файл .htaccess. Вот такая вот этажерка получается.
В простейшем случае директория не меняется, но алгоритм работы остаётся для всех случаев единым, и вновь найденный в этой же директории тот же самый файл .htaccess, хотя и остаётся прежним, но выполняется повторно, как будто это другой файл другой директории. Сервер не различает эти файлы в разных директориях, и тупо действует по одному сценарию. И это несмотря на то, что подавляющее большинство сайтов использует только один файл настроек в корневой директории.
Очевидно, что так сделано в угоду программистам, писавшим сервер, а не для удобства пользователей. Ведь сервер вполне мог бы различать файлы .htaccess, лежащие в разных директориях, и не выполнять скрипты, если файл со скриптами оказался тот же самый. Но данность такова, что мы должны приспосабливаться к такому поведению сервера.
Правила в .htaccess
Помимо трудностей, вызванных многократностью прохождения запроса через скрипт, существует трудность, связанная с эргономикой написания самих правил. Впоследствии мы будем применять специальные приёмы, которые позволят немного исправить ситуацию в обоих случаях, а пока познакомимся с характерными настройками файла .htaccess.
В самом начале файла настроек следует разрешить использовать в скрипте символические ссылки, ведущие за пределы корня сайта. Без включения этой опции модуль сервера mod_rewrite, который мы будем использовать для модификации запросов, банально не будет работать. Это факт, который невозможно понять. Его надо просто запомнить. Включаем символические ссылки командой:
Options +FollowSymLinks
Правила скрипта желательно заключить в блок условия, проверяющего доступность модуля сервера, который будет их интерпретировать. Модуль mod_rewrite является модулем, включённым по умолчанию в состав сервера Apache но, тем не менее, не лишним будет проверить его наличие, иначе, при отсутствии этого модуля по каким-либо причинам, сервер вернёт ошибку 500 (внутренняя ошибка сервера). Блок условия выглядит следующим образом:
<IfModule mod_rewrite.c>
# Сюда вставляются правила
</IfModule>
Первыми двумя строчками внутри блока включается действие модуля и устанавливается базовая директория (обычно это директория, в которой расположен текущий файл .htaccess). В нашем случае, пусть это будет корень сайта, обозначаемый прямым слешем:
RewriteEngine On
RewriteBase /
А вот дальше уже следуют сами правила. В рассматриваемом нами случае каждое правило будет состоять из связки директив RewriteRule и RewriteCond. В большинстве случаев их вполне достаточно. Директива RewriteRule проверяет первичное условие и осуществляет замену строки запроса, а директивы RewriteCond, которых может быть несколько, а может и не быть вовсе, проверяют вторичные условия, если таковые существуют. При этом директивы RewriteCond записываются непосредственно перед той директивой RewriteRule, к которой относятся. Так формируется блок правила.
Такая запись правил, как я уже говорил, вызывает много вопросов к эргономике. Выполнение директив происходит как бы «домиком». Сначала выполняется первая часть нижней директивы блока (RewriteRule), где происходит первичная проверка условия, затем верхняя и все последующие директивы RewriteCond, а затем вторая часть нижней директивы RewriteRule, которая и осуществляет замену запроса. Запись среднестатистического правила выглядит примерно так:
RewriteCond %{HTTP_HOST} ^www\.(.*) [NC]
RewriteRule (.*) http://%1/$1 [R=301,L]
Это правило меняет пользовательский запрос, убирая www из доменного имени. Рассмотрим логику работы этого правила.
Когда строка запроса от браузера или поисковика попадает в правило, и из неё автоматически вычленяется только путь к файлу или директории, который и подлежит модификации, и передаётся директиве RewriteRule. Например, если на вход в скрипт пришёл запрос от браузера:
GET articles/histoty/papyruses.html HTTP/1.1
Host: www.mysite.ru
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept: text/html
Connection: close
то на обработку в RewriteRule попадает только «articles/histoty/papyruses.html». Всё остальное распихивается по специальным переменным. Например, mysite.ru уходит в переменную %{HTTP_HOST}, а в переменную %{THE_REQUEST} уходит целиком вся первая строка запроса: «GET articles/histoty/papyruses.html HTTP/1.1». Подобным образом заполняются и другие переменные, о которых мы упомянем позже при необходимости.
Правило выполняется в следующей последовательности:
- Путь к файлу (articles/histoty/papyruses.html) передаётся директиве RewriteRule, и сравнивается с шаблоном, описанным регулярным выражением в первом параметре директивы. Регулярное выражение имеет синтаксис, использующийся в языке Perl. В нашем примере под шаблон попадает любая строка. Круглые скобки выделяют часть шаблона (в нашем случае весь шаблон), содержимое которого отправиться во временную переменную $1. В нашем случае туда отправится вся строка.
- Если строка совпадает с шаблоном, выполняется директива RewriteCond. В директиве сравнивается первый её параметр (содержимое переменной %{HTTP_HOST}, например, www.mysite.ru) с шаблоном, заданным регулярным выражением во втором её параметре. Здесь тоже круглыми скобками выделяется часть шаблона, содержимое которого отправляется в другую временную переменную %1. В третьем параметре установлен флаг NC (NoCase) — в регулярном выражении не учитывать регистр букв.
- Если первый параметр совпадает с шаблоном, выполняется замена поданной на вход правила строки на строку, заданную во втором параметре директивы RewriteRule. Строка формируется из содержимого обеих переменных. Флаг R=301 в третьем параметре директивы сообщает, что после завершения скрипта необходимо произвести постоянный редирект по вновь сформированному адресу (о сущности редиректов я расскажу позже), а флаг L приказывает скрипту завершиться сейчас же, в результате чего редирект произойдёт сразу же, а не по завершении всего скрипта.
Наиболее используемыми являются следующие флаги:
- L (Last) — Прервать выполнение текущего прохода скрипта (если указан вместе с R, то тут же происходит редирект).
- R (Redirect) — Выполнить постоянный (301) внешний редирект на полученный URL.
- NC (NoCase) — В регулярном выражении не учитывать регистр букв.
- OR (or) — Выполнять правило, если соблюдено либо текущее, либо следующее условие.
- F (Forbidden) — Блокировать переход, отправить пользователю ответ со статусом 403 (Forbidden).
- S (skip) — Если правило нашло соответствие, пропустить указанное количество следующих правил.
- C (chain) — Если правило не нашло соответствие, пропустить весь оставшийся блок правил, связанный этим же флагом.
- E (Env) — Создать переменную среды окружения и занести в неё значения: E=переменная:значение.
При этом директива RewriteCond из всех этих флагов может использовать только флаги NC и OR.
Наиболее используемыми являются следующие переменные:
- %{HTTP_HOST} — домен сайта из запроса (например: www.site.ru).
- %{REQUEST_URI} — ресурс, запрошенный в первой строке запроса (например: /articles/article1.html).
- %{QUERY_STRING} — строка параметров, которая располагается в переданной ссылке после знака вопроса (например: page=articles/article1.html).
- %{REQUEST_FILENAME} — полный путь в файловой системе сервера к файлу, соответствующий запрошенному ресурсу (например: Z:/home/site.ru/public_html/articles/article1.html).
В этой части мы получили первичное представление о сущности правил. В следующей части мы определим задачу и увидим конкретный пример её реализации.