Настройка сайта на web-сервере с помощью .htaccess, показанная на реальном примере

Новиков М.Г.
28.04.2015

Часть 1 — Часть 2Часть 3

Часть 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, так что с их размещением ничего сложного нет. Сложности наступают позже, при написании скрипта. Дело в том, что язык правил не очень эргономичен, и зачастую веб-мастера его элементарно не понимают. Интернет полон копипастами фрагментов скриптов, выполняющих то или иное действие, но мало где даётся подробная расшифровка того, что они делают. Одно из приятных исключений, статья:

http://habrahabr.ru/company/sprinthost/blog/129560/

в которой как раз-таки очень доступно описывается логика работы команд скрипта. Другим полезным источником может служить официальная документация по Apache:

http://httpd.apache.org/docs/current/mod/mod_rewrite.html

Так что я настоятельно рекомендую изучить эти источники.

Скрипт, записанный в .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». Подобным образом заполняются и другие переменные, о которых мы упомянем позже при необходимости.

Правило выполняется в следующей последовательности:

  1. Путь к файлу (articles/histoty/papyruses.html) передаётся директиве RewriteRule, и сравнивается с шаблоном, описанным регулярным выражением в первом параметре директивы. Регулярное выражение имеет синтаксис, использующийся в языке Perl. В нашем примере под шаблон попадает любая строка. Круглые скобки выделяют часть шаблона (в нашем случае весь шаблон), содержимое которого отправиться во временную переменную $1. В нашем случае туда отправится вся строка.
  2. Если строка совпадает с шаблоном, выполняется директива RewriteCond. В директиве сравнивается первый её параметр (содержимое переменной %{HTTP_HOST}, например, www.mysite.ru) с шаблоном, заданным регулярным выражением во втором её параметре. Здесь тоже круглыми скобками выделяется часть шаблона, содержимое которого отправляется в другую временную переменную %1. В третьем параметре установлен флаг NC (NoCase) — в регулярном выражении не учитывать регистр букв.
  3. Если первый параметр совпадает с шаблоном, выполняется замена поданной на вход правила строки на строку, заданную во втором параметре директивы 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).

В этой части мы получили первичное представление о сущности правил. В следующей части мы определим задачу и увидим конкретный пример её реализации.

[Вернуться в начало]