Коды ответов (ошибок) HTTP сервера

При любом HTTP запросе сервер сначала возвращает код ответа на HTML запрос. Проанализировав этот ответ можно сделать вывод о том, был ли запрос выполнен успешно, или в процессе обработки запроса произошла ошибка.

Коды ответа HTTP сервера могут принадлежать следующим группам:
1xx — Информационный ответ
2xx — Успешная обработка запроса
3xx — Переадресация (редирект)
4xx — Неполный запрос к серверу
5xx — При обработке запроса произошла ошибка

Естественно, что кодов ответа в каждой группе не по 100. Браузер (клиент IE, Opera, Mozilla и т.д.), получая от сервера тот или иной код, сам решает как его интерпретировать. Если код клиенту неизвестен, то как минимум он может определить диапазон кода и повести себя соответствующим образом.

Информационные ответы

100 Continue
Часть запроса принята. Как правило это занчит, что можно отправлять следующую часть запроса.

101 Switching Protocols
Сервер производит переключение протоколов в соответствии с заголовком Upgrade.

Успешная обработка запроса

200 OK
Запрос обработан успешно.

201 Created
Данный код используется когда происходит создание нового URI. Вместе с кодом сервер посылает заголовок Location с адресом нового URI.

202 Accepted
Запрос принят и обрабатывается. В теле ответа как правило содержится дополнительная информация.

203 Non-Authoritative Information
Ответ означает, что информация получена из ненадежного источника (например, с другого сервера).

204 No Content
Запрос обработан, но в ответ ничего не возвращается. Как правило используется если в ответ на запрос не нужно обновлять содержимое документа.

205 Reset Content
Означает, что содержимое документа должно быть сброшено в начальное состояние. Обычно используется при очистке форм ввода данных..

206 Partial Content
При данном ответе возвращается лишь часть данных. Обычно используется если клиент запросил часть данных с использованием заголовка Range.

Переадресация

300 Multiple Choices
Означает, что существует несколько вариантов запрашиваемой страницы. Например, сайт, переведенный на несколько языков.

301 Moved Permanently
Данный ответ означает, что данный документ был перемещен и клиенту следует изменить все ссылки на данный документ его новым местоположением.

302 Moved Temporarily
Документ временно перемещен в другое место.

303 See Other
Данный документ можно найте по другим ссылкам. Список ссылок передан в теле ответа.

304 Not Modified
Данный код ответа возвращается если был запрос lf-Modified-Since, и документ не изменялся с указанной даты.

305 Use Proxy
Доступ к документу должен осуществляться через proxy-сервер, адрес которого указан в Location.

Неполные запросы клиента

400 Bad Request
Ошибка в строке запроса.

401 Unauthorized
Ответ означает, что пользователь не имеет достаточных прав для просмотра документа.

402 Payment Required
Данный код зарезервирован на будущее. Видимо будет означать, что запрошенный документ является платным ресурсом.

403 Forbidden
Запрос не будет выполнен по какой-либо причине.

404 Not Found
Запрашиваемого документа нет на сервере.

405 Method Not Allowed
Означает, что метод, используемый клиентом, не поддерживается.

406 Not Acceptable
Ресурс существует, но не в той форме, что клиент запросил. Например, может различаться язык документа.

407 Proxy Authentication Required
Для Proxy-сервера необходима авторизация.

408 Request Time-out
Сервер разорвал соединение из-за превышенного таймаута.

409 Conflict
Запрос конфликтует с другим запросом.

410 Gone
Данный код означает, что документ был удален с сервера.

411 Length Required
Пропущено необходимое поле в заголовке запроса Content-Length.

412 Precondition Failed
Условие указанное в заголовке не выполняется.

413 Request Entity Too Large
Слишком большое тело запроса.

414 Request-URI Too Long
Слишком длинный URI в запросе.

415 Unsupported Media Type
Сервер не поддерживает указанный формат данных.

Ошибки сервера

500 Internal Server Error
Внутренняя ошибка сервера. Например, ошибка при выполнении скрипта.

501 Not Implemented
Недопустимое действие.

502 Bad Gateway
Недопустимый ответ с другого ресурса.

503 Service Unavailable
Данный код означает, что указанный сервис временно недоступен.

504 Gateway Time-out
Превышен таймаут ожидания от другого ресурса

505 HTTP Version not supported
Данная версия протокола HTTP не поддерживается сервером.

Умное Кеширование и Версионность в Javascript/CSS

Подключая внешние CSS и Javascript, мы хотим снизить до минимума лишние HTTP-запросы.

Для этого .js и .css файлы отдаются с заголовками, обеспечивающими надежное кеширование.

Но что делать, когда какой-то из этих файлов меняется в процессе разработки? У всех пользователей в кеше старый вариант — пока кеш не устарел, придет масса жалоб на сломанную интеграцию серверной и клиентской части.

Правильный способ кеширования и версионности полностью избавляет от этой проблемы и обеспечивает надежную, прозрачную синхронизацию версий стиля/скрип.

Простое кеширование ETag

Самый простой способ кеширования статических ресурсов — использование ETag.

Достаточно включить соответствующую настройку сервера (для Apache включена по умолчанию) — и к каждому файлу в заголовках будет даваться ETag — хеш, который зависит от времени обновления, размера файла и (на inode-based файловых системах) inode.

Браузер кеширует такой файл и при последующих запросах указывет заголовок If-None-Match с ETag кешированного документа. Получив такой заголовок, сервер может ответить кодом 304 — и тогда документ будет взят из кеша.

Выглядит это так:

Первый запрос к серверу (кеш чистый)

GET /misc/pack.js HTTP/1.1
Host: javascript.ru

Вообще, браузер обычно добавляет еще пачку заголовоков типа User-Agent, Accept и т.п. Для краткости они порезаны.

Ответ сервераСервер посылает в ответ документ c кодом 200 и ETag:

HTTP/1.x 200 OK
Content-Encoding: gzip
Content-Type: text/javascript; charset=utf-8
Etag: "3272221997"
Accept-Ranges: bytes
Content-Length: 23321
Date: Fri, 02 May 2008 17:22:46 GMT
Server: lighttpd

Следующий запрос браузера
При следующем запросе браузер добавляет If-None-Match: (кешированный ETag):

GET /misc/pack.js HTTP/1.1
Host: javascript.ru
If-None-Match: "453700005"

Ответ сервера
Сервер смотрит — ага, документ не изменился. Значит можно выдать код 304 и не посылать документ заново.

HTTP/1.x 304 Not Modified
Content-Encoding: gzip
Etag: "453700005"
Content-Type: text/javascript; charset=utf-8
Accept-Ranges: bytes
Date: Tue, 15 Apr 2008 10:17:11 GMT

Альтернативный вариант — если документ изменился, тогда сервер просто посылает 200 с новым ETag.

Аналогичным образом работает связка Last-Modified + If-Modified-Since:

1. сервер посылает дату последней модификации в заголовке Last-Modified (вместо ETag)
2. браузер посылает дату закешированной версии в заголовке If-Modified-Since(вместо If-None-Match)
3. Если скрипт не изменился — ответ содержит только код 304

Эти способы работают стабильно и хорошо, но браузеру в любом случае приходится делать по запросу для каждого скрипта или стиля.

Умное кеширование. Версионность

Общий подход для версионности — в двух словах:

1. Во все скрипты добавляется версия (или дата модификации). Например, http://javascript.ru/my.js превратится в http://javascript.ru/my.v1.2.js
2. Все скрипты жестко кешируются браузером
3. При обновлении скрипта версия меняется на новую: http://javascript.ru/my.v2.0.js
4. Адрес изменился, поэтому браузер запросит и закеширует файл заново
5. Старая версия 1.2 постепенно выпадет из кеша

Дальше мы разберем, как сделать этот процесс автоматическим и прозрачным.

Жесткое кеширование

Жесткое кеширование — своего рода кувалда которая полностью прибивает запросы к серверу для кешированных документов.

Для этого достаточно добавить заголовки Expires и Cache-Control: max-age.

Например, чтобы закешировать на 365 дней в PHP:

header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT");
header("Cache-Control: max-age="+86400*365);

Или можно закешировать контент надолго, используя mod_header в Apache:

Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT"
Header add "Cache-Control" "max-age=315360000"

Получив такие заголовки, браузер жестко закеширует документ надолго. Все дальнейшие обращения к документу будут напрямую обслуживаться из кеша браузера, без обращения к серверу.

Большинство браузеров (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими. Именно поэтому мы добавляем версию в имя файла. Конечно, с такими адресами приходится использовать решение типа mod_rewrite, мы это рассмотрим дальше в статье.P.S А вот Firefox кеширует адреса с вопросительными знаками.

Автоматическое преобразование имен

Разберем, как автоматически и прозрачно менять версии, не переименовывая при этом сам файлы.

Имя с версией -> Файл

Самое простое — это превратить имя с версией в оригинальное имя файла.

На уровне Apache это можно сделать mod_rewrite:

RewriteEngine on
RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$    /$1$2    [L]

Такое правило обрабатывает все css/js/gif/png/jpg-файлы, вырезая из имени версию.

Например:

/images/logo.v2.gif -> /images/logo.gif
/css/style.v1.27.css -> /css/style.css
/javascript/script.v6.js -> /javascript/script.js

Но кроме вырезания версии — надо еще добавлять заголовки жесткого кеширования к файлам. Для этого используются директивы mod_header:

Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT"
Header add "Cache-Control" "max-age=315360000"

А все вместе реализует вот такой апачевый конфиг:

RewriteEngine on
# убирает версию, и заодно ставит переменную что файл версионный
RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$ /$1$2 [L,E=VERSIONED_FILE:1]
 
# жестко кешируем версионные файлы
Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE
Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE

Из-за порядка работы модуля mod_rewrite, RewriteRule нужно поставить в основной конфигурационный файл httpd.conf или в подключаемые к нему(include) файлы, но ни в коем случае не в .htaccess, иначе команды Header будут запущены первыми, до того, как установлена переменная VERSIONED_FILE. Директивы Header могут быть где угодно, даже в .htaccess — без разницы.

Автоматическое добавление версии в имя файла на HTML-странице

Как ставить версию в имя скрипта — зависит от Вашей шаблонной системы и, вообще, способа добавлять скрипты (стили и т.п.).

Например, при использовании даты модификации в качестве версии и шаблонизатора Smarty — ссылки можно ставить так:

<link href="{version src='/css/group.css'}" rel="stylesheet" type="text/css" />

Функция version добавляет версию:

function smarty_version($args){
 
  $stat = stat($GLOBALS['config']['site_root'].$args['src']);
  $version = $stat['mtime'];
 
  echo preg_replace('!\.([a-z]+?)$!', ".v$version.\$1", $args['src']);
}

Результат на странице:

<link href="/css/group.v1234567890.css" rel="stylesheet" type="text/css" />

Отпимизация

Чтобы избежать лишних вызовов stat, можно хранить массив со списком текущих версий в отдельной переменной

$versions['css'] = array(
  'group.css' => '1.1',
  'other.css' => '3.0',
}

В этом случае в HTML просто подставляется текущая версия из массива.

Можно скрестить оба подхода, и выдавать во время разработки версию по дате модификации — для актуальности, а в продакшн — версию из массива, для производительности.

Применимость

Такой способ кеширования работает везде, включая Javascript, CSS, изображения, flash-ролики и т.п.

Он полезен всегда, когда документ изменяется, но в браузере всегда должна быть текущая актуальная версия.

Основы модуля mod_rewrite Apache

Модуль mod_rewrite является программным модулем веб сервера Apache (обратите внимание, что он не будет выполняться под другими веб-серверами). Его первичная функция — манипуляция действий с URL. Модуль очень универсален и разносторонен, поэтому я постараюсь
показать здесь множество реальных примеров.

Mod_rewrite является замечательным модулем, который предоставляет основанный на некоторых правилах механизм динамического изменения запрашиваемых ссылок. Это действительно мощный инструмент, и поэтому, его знание принципиально важно, если вы хотите стать подлинным веб мастером или веб программистом. Не столько принципиально, будете ли вы использовать его в своей работе, сколько важно то, что вы знаете, что он может делать, и сможете поведать об этом своему боссу, когда появится желание сделать что-нибудь странное с веб сервером.

Однако нужно быть очень осторожным и даже дотошным при работе с этим модулем! Некоторые ошибки, которые Вы способны допустить, могут привести к логической петле, причиняя непрекращающуюся 100%-ую загрузку ценрального процессора (CPU).

Чтобы не казаться пространным в рассуждениях, приведу некоторые очень простые примеры.

Прежде, чем мы сможем приступить к работе, Вы должны будете проверить, установлен ли модуль на вашем веб сервере или нет.

Есть несколько способов проверить это:

  1. Спросить вашего системного администратора — знает ли он (или она) о наличии этого модуля на веб сервере. Они действительно должны знать, но как показывает практика — попадаются и не очень сведующие сисадмины. Не напрягайте других: если Вы используете ваш веб сервер с сотнями других доменов, ваши действия могут разбудить некоторых спящих
    собак, поскольку использование mod_rewrite будет всегда влечь за собой некоторую увеличенную загрузку ценрального процессора.
  2. Проверить ваш файл конфигурации Apache (httpd.conf), если вы имеете к нему доступ. Один из возможных стандартных путей может быть: /etc/httpd/httpd.conf. Однако, ваш путь может очевидно отличаться от этого.
  3. Проверить работу вашего сервера с нижеприведенными примерами. Если
    сервер работает без ошибок — mod_rewrite действительно установлен на вашей системе. Если нет, Вы получите следующее сообщение при запросе любой web-страницы с вашего сервера: «Внутренняя ошибка сервера». Также, вы увидите такую запись в файле «error.log»:
    «Invalid command RewriteEngine, perhaps mis-spelled or defined by a module not included in the server configuration».

Теперь давайте копнем поглубже и посмотрим первый практический примерчик.

Предположим, что Вы будете использовать mod_rewrite только для вашего собственного сайта, то есть не как обобщенную перекрестную установку сервера.

Для нашего примера потребуется использование файла .htaccess. Для работы этого метода, вы должны загрузить файл под названием «.htaccess» (пожалуйста, обратите внимание на точку в начале имени файла!) в папку сервера, с которой Вы будете работать. Это можно сделать через telnet или ftp. (Предупреждение: .htaccess должен быть загружен в ‘режиме ASCII’, то есть не в двоичном режиме!)

Если у Вас уже имеется файл «.htaccess», например со следующими записями:

Options Includes +ExecCGI
AddType text/x-server-parsed-html .html

то просто добавьте снизу наш образец кода к уже существующему (важно: редактируйте ваш файл .htaccess в ASCII-редакторе типа Notepad).

Первые две записи запустят сам модуль:

RewriteEngine on
Options +FollowSymlinks

Совет: запись «RewriteEngine off» отменит все последующие команды. Это
очень полезная особенность — вместо необходимости комментировать все
последующие строки — все, что Вы должны сделать, это установить «off».

Если ваш системный администратор запрещает Вам использование «Options
+FollowSymlinks», Вы не сможете ограничить использование mod_rewrite
для отдельных каталогов, вместо этого изменения будут действовать на
весь сервер.

Следующая необходимая запись:

RewriteBase /

«/» является корневым (основным) URL. Если у вас какой-то другой URL, то вы можете указать это в данной директиве, однако «/» — обычно эквивалентно адресу «http://домен.ru».

А теперь, господа, перейдем к более интересным записям!

Предположим, что вы хотите защитить от несанкционированного доступа
ваш файл .htaccess. На некоторых серверах вы можете легко читать этот
файл просто вводя URL следующего формата в поле адреса вашего
браузера: http://www.domain.com/.htaccess — серьезное упущение защиты,
так как содержание вашего .htaccess может показать важную информацию
об установках и настройках вашего сайта человеку, знающему как эти
знания применить против вас.

Чтобы блокировать этот доступ, запишем следующее:

RewriteRule ^.htaccess$ - [F]

Это правило переводится так:

Если кто-то пробует обращаться к файлу .htaccess, система должна выдать код ошибки «HTTP response of 403» или «403 Forbidden — You don’t have permission to access /.htaccess on this server».

Конструкция ^.htaccess$ в этом регулярном выражении означает:

  • ^ — якорь начала строки;
  • $ — якорь конца строки;
  • . — в регулярных выражениях точка «.» обозначает мета-символ и должна быть защищена обратным слэшем (backslash), если вы все-таки хотите использовать именно фактическую точку.
  • Имя файла должно быть расположено точно между начальным и конечным якорем. Это будет гарантировать то, что только это определенное имя файла и никакое другое, сгенерирует код ошибки.
  • [F] — специальный «запрещающий» флажок (forbidden).

В этом примере, файл «.htaccess» теперь будет состоять из таких строк:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^.htaccess$ - [F]

Если мы добавим наш код (в примерах) к существовавшему ранее файлу «.htaccess», то получим следующую конструкцию:

Options Includes +ExecCGI
AddType text/x-server-parsed-html .html
RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^.htaccess$ - [F]

Это «правило» глобально, то есть каждый получит указанное сообщение об ошибке. Напомню, что mod_rewrite является модулем, который предоставляет основанный на некоторых правилах механизм динамического изменения запрашиваемых ссылок.

Мы можем ограничивать «правило» при помощи различных «условий правила». «Правило» будет выполнено только в том случае, если перед ним будет встречен ряд условий.

Синтаксис: условие должно предшествовать правилу!

Возьмем еще один пример (запись в файле .htaccess):

RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} ^EmailSiphon
RewriteRule ^.*$ - [F]

Назначение первых трех записей было подробно разобрано в первой части публикации. Их функция — включение «движка перезаписи», то есть самого модуля.

Последние две строки запрещают доступ поисковому роботу под кодовым названием «EmailSiphon» (имеется ввиду имя пользовательского агента). Данный робот является сборщиком почтовых адресов с различных веб страниц.

Строка:

RewriteCond %{HTTP_USER_AGENT} ^EmailSiphon

состоит из трех частей:

  • Директива (указание): RewriteCond;
  • Проверочная строка: %{HTTP_USER_AGENT};
  • Образец условия: ^EmailSiphon.

Проверочная строка — переменная сервера, которая может быть записана в общей форме: ‘% {ИМЯ_ПЕРЕМЕННОЙ}’.

Образец условия — регулярное выражение. Для более полного понимания темы стоит рассмотреть регулярные выражения как класс.

Регулярные выражения — это механизм, позволяющий задать шаблон для строки и осуществить поиск данных, соответствующих этому шаблону в заданном тексте. Кроме того, дополнительные функции по работе с такими выражениями позволяют получить найденные данные в виде массива строк, произвести замену в тексте по шаблону, разбиение строки по шаблону и т.п. Однако главной их функцией, на которой основаны все остальные, является именно функция поиска в тексте данных, соответствующих шаблону (образцу), описанному в синтаксисе регулярных выражений.

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

Например, регулярное выражение:

s/abc/xyz/g

заменит строку ‘abc’, на строку ‘xyz’ во всем тексте.

Вот краткий обзор наиболее важных элементов с некоторыми примерами:

  • . (точка) — текст (любой символ);
  • | — чередование (то есть/abc|def/);
  • * — квантификатор (разрешено любое число);
  • ^ $ — якоря строки;
  • s — оператор (string1 заменить на string2);
  • g — модификатор (искать по всему тексту).

Регулярные выражения конструируются с помощью этих элементов и других
«обычных» символов. Они не являются отдельным языком, а используются другими средствами, например языками программирования типа Perl или PHP, а также текстовыми редакторами (Emacs).

Если говорить о связи регулярных выражений и модуля mod_rewrite, то они используются в директивах RewriteRule и RewriteCond.

«^» обозначает начало строки. Из этого следует, что User-agent должен начинаться со строки «EmailSiphon» и ни с чего другого («NewEmailSiphon», например, не работал бы).

Но, поскольку данное регулярное выражение не содержит символ «$» (якорь конца строки), User-agent мог бы быть, например, «EmailSiphon2».

Последняя строка нашего примера:

RewriteRule ^.*$ - [F]

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

Регулярное выражение ‘^.*$’ означает «Доступ ко всем файлам запрещен».

Точка «.» в регулярном выражении — мета символ (подстановочный знак), означающий любой случайный символ.

«*» означает то, что строка может встречаться неограниченное количество раз. В этом случае, независимо от имени запрошенного файла, будет выдана ошибка.

«EmailSiphon», конечно, не единственный почтовый сборщик. Другой известный член этого семейства — ‘ExtractorPro’. Допустим мы хотим запретить доступ и этому роботу. В таком случае нам необходимо еще одно условие.

Теперь файл .htaccess будет выглядеть так:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} ^EmailSiphon [OR]
RewriteCond %{HTTP_USER_AGENT} ^ExtractorPro
RewriteRule ^.*$ - [F]

Третий аргумент [OR] (в первой строке RewriteCond) называется «флагом». Существуют два возможных флага:

NC — не учитывать регистр букв.
OR — означает «или следующее условие».

Флажок NC позволяет игнорировать регистр букв в искомом образце. Например:

RewriteCond %{HTTP_USER_AGENT} ^emailsiphon [NC]

Эта строка определяет, что и «emailsiphon» и «EmailSiphon» будут признаны как идентичные выражения.

Вы можете использовать сразу несколько флажков, разделяя их запятыми.

RewriteCond % {HTTP_USER_AGENT} ^EmailSiphon [NC, OR]
RewriteCond % {HTTP_USER_AGENT} ^ExtractorPro

Нет никаких ограничений по числу условий. Таким образом, Вы можете блокировать 10, 100, 1000 или более известных почтовых сборщиков. Определение этих 1000 условий — просто вопрос загрузки сервера и прозрачности файла «.htaccess».

В вышеупомянутом примере используется глобальная переменная «HTTP_USER_AGENT». Существуют также другие переменные:

  • REMOTE_HOST;
  • REMOTE_ADDR.

Например, если вы хотите заблокировать паука пришедшего с www.site.ru, вы можете использовать глобальную переменную «REMOTE_HOST» таким образом:

RewriteCond % {REMOTE_HOST} ^www.site.ru$
RewriteRule ^.*$ - [F]

Если вы хотите заблокировать определенный IP-адрес, условие будет выглядеть так:

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

В регулярном выражении по проверке точного и полного IP-адреса нужно использовать начальные и конечные якоря. Также можно исключить целый диапазон:

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

Этот пример показывает, как можно заблокировать диапазон IP адресов с 212.37.64.0 по 212.37.64.255.

А вот маленькая задачка для проверки приобретенных знаний (решение будет дано в следующей части):

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

Внимание, вопрос!

Если мы пишем в регулярном выражении «^212.37.64» вместо «^212.37.64.» (с точкой в конце), то даст ли это тот же самый эффект, и будут ли исключены те же самые IP адреса?

Вот решение задачки:

Если мы пишем в регулярном выражении «^212.37.64» вместо «^212.37.64.» (с точкой в конце), то даст ли это тот же самый эффект, и будут ли исключены те же самые IP адреса?

Регулярное выражение ^212.37.64 удовлетворяет и применимо к следующим строкам:

212.37.64
212.37.640
212.37.641
212.37.64a
212.37.64abc
212.37.64.12
212.37.642.12

Следовательно, последняя цифра «4» может сопровождаться любой символьной строкой. Однако, максимальным значением IP является адрес 255.255.255.255 который подразумевает, что например 212.37.642.12 неправильный (недопустимый) IP. Единственный допустимый IP в
вышеприведенном списке — 212.37.64.12.

Предположим, что у нас есть виртуальный магазин по продаже каких-то товаров. Клиенты обращаются к описаниям товаров через скрипт:

http://www.yoursite.com/cgi-bin/shop.cgi?product1
http://www.yoursite.com/cgi-bin/shop.cgi?product2
http://www.yoursite.com/cgi-bin/shop.cgi?product3

Эти адреса представлены как ссылки на большинстве страниц сайта.

А теперь допустим, что вы решили добавить сайт для индексации в поисковые системы. Тут вас поджидает небольшая неприятность — не все поисковики принимают, понимают и индексируют URL, в которых содержится символ «?». Более естественным и приемлемым для поисковика является URL вида:

http://www.yoursite.com/cgi-bin/shop.cgi/product1

В данном случае символ ‘?’ заменяется на ‘/’. Еще более комфортабельный URL с точки зрения поисковика будет иметь вид:

http://www.yoursite.com/shop/product1

Для поисковика, «shop» теперь как-бы является директорией, содержащей товары product1, product2 и т.д.

Если пользователь, со страницы результатов запроса в поисковике проследует по такой ссылке, то эта ссылка должна будет трансформироваться в ссылку /shop.cgi?product1.

Чтобы добиться такого эффекта можно использовать mod_rewrite, используя следующую конструкцию в файле .htaccess:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^(.*)shop/(.*)$ $1cgi-bin/shop.cgi?$2

Переменные $1 и $2 составляют так называемые обратные ссылки. Они связаны с текстовыми группами. Вызываемый URL разбивается на части. Все, что находится перед «shop», плюс все что находится после «shop/» определяется и хранится в этих двух переменных: $1 и $2.

До этого момента, наши примеры использовали ‘правила’ типа:

RewriteRule ^.htaccess*$ - [F]

Однако мы еще не достигли истинной перезаписи URL адресов, в смысле того, что один URL должен перенаправлять посетителя на другой.

Для нашей записи вида:

RewriteRule ^(.*)shop/(.*)$ $1cgi-bin/shop.cgi?$2

применяется общий синтаксис:

RewriteRule текущийURL перезаписываемыйURL

Как видите, эта директива выполняет действительную «перезапись» URL адреса.

В дополнение к записям в файл .htaccess, нужно еще заменить все ссылки на сайте, которые имеют формат «cgi-bin/shop.cgi?product», на ссылки вида «shop/product».

Теперь, когда поисковик найдет страницу с подобными ссылками, он проиндексирует сайт без всяких видимых проблем.

Таким образом вы можете превратить чисто динамический сайт в сайт, имеющий статическую структуру, что явно принесет пользу в вопросе индексирования различными посковыми машинами. Обратите внимание на вид URL адресов на данном сайте. Вдобавок ко всему, они имеют еще и
легкочитамую для человека структуру — ЧПУ (человекопонятный УРЛ). Но об этом мы поговорим в другой статье.

В нашем втором примере мы обсудим, как переадресовать запросы «.txt» файлов к сценарию программы.

Многие хостинг провайдеры, работающие с Apache предоставляют лог-файлы в общем формате. Это означает то, что они не будут соджержать поля с ссылающимися страницами и юзер-агентами.

Однако, относительно запросов к файлу «robots.txt», предпочтительно иметь доступ ко всем этим данным, чтобы иметь больше информации о посещении поисковиков, чем просто знать их IP-дреса. Для того, чтобы оганизовать это, в «.htaccess» должны быть следующие записи:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^robots.txt$ /text.cgi?%{REQUEST_URI}

Теперь при запросе файла «robots.txt» наш RewriteRule переадресует посетителя (робота) к обрабатывающему запросы скрипту text.cgi. Кроме того, переменная передается скрипту, которая будет обработана в соответствии с вашими нуждами. «REQUEST_URI» определяет имя запрашиваемого файла. В данном примере это — «robots.txt». Скрипт прочтет содержание «robots.txt» и отправит его web-браузеру или роботу поискового сервера. Таким образом, мы можем считать хиты посетителей и вести свои лог-файлы.

С этой целью, скрипт будет использовать переменные окружения $ENV {‘HTTP_USER_AGENT’}’ и т.д. Это обеспечит получение всей требуемой информации. Вот исходный текст для сценария cgi, упомянутого выше (пример взят с сайта http://fantomaster.com):

#!/usr/bin/perl
# If required, adjust line above to point to Perl 5.
#################################
# (c) Copyright 2000 by fantomaster.com #
# All rights reserved. #
#################################
$stats_dir = "stats";
$log_file = "stats.log";
$remote_host = "$ENV{'REMOTE_HOST'}";
$remote_addr = "$ENV{'REMOTE_ADDR'}";
$user_agent = "$ENV{'HTTP_USER_AGENT'}";
$referer = "$ENV{'HTTP_REFERER'}";
$document_name = "$ENV{'QUERY_STRING'}";
open (FILE, "robots.txt");
@TEXT = ;
close (FILE);
&amp;get_date;
&amp;log_hits("$date $remote_host $remote_addr $user_agent $referer $document_name");
print "Content-type: text/plain
";
print @TEXT;
exit;
sub get_date {
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
$mon++;
$sec = sprintf ("%02d", $sec);
$min = sprintf ("%02d", $min);
$hour = sprintf ("%02d", $hour);
$mday = sprintf ("%02d", $mday);
$mon = sprintf ("%02d", $mon);
$year = scalar localtime;
$year =~ s/.*?(d{4})/$1/;
$date="$year-$mon-$mday, $hour:$min:$sec";
}
sub log_hits {
open (HITS, "&gt;&gt;$stats_dir/$log_file");
print HITS @_;
close (HITS);
}

Загрузите файл с данным содержимым в корневую или в DocumentRoot директорию сервера и установите права доступа у файлу (chmod) 755. Затем, создайте каталог «stats». Более детальное описание о том, как установить скрипт вы можете получить на сайте разработчика. Если настройки вашего сервера не позволяют исполнять cgi-сценарии в главной директории (DocumentRoot), то попробуйте следующий вариант:

RewriteRule ^robots.txt$ /cgi-bin/text.cgi?%{REQUEST_URI}

Обратите внимание, что в этом случае, будет необходимо изменить пути в коде скрипта!

Если вы хотите вести логи всех операций, выполненных с помощью mod_rewrite, можно активировать это с помощью следующей записи:

RewriteLog /usr/local/apache/logs/mod_rewrite_log
RewriteLogLevel 1

Эту строку нужно вписать в «Раздел 2: Конфигурация основного сервера» в файле httpd.conf, а не в .htaccess!

Все манипуляции, произведенные mod_rewrite будут записываться в этот файл. Имя лог файла может быть любым. Вы можете указать абсолютный или относительный (относительно ServerRoot) путь к файлу.

Если вы хотите вести разные лог файлы для различных виртуальных хостов, то нужно ввести изменения в «Раздел 3: Виртуальные сервера», например так:

ServerAdmin webmaster@yourdomain.com
DocumentRoot /usr/www/htdocs/yourdomain
ServerName yourdomain.com
RewriteLog /usr/apache/logs/yourdomain_mod_rewrite_log
RewriteLogLevel 1

RewriteLogLevel может быть определен в пределах от 1 до 8. Обычно достаточно первого уровня. Более высокие уровни используются для отладки.

Другая директива, которая является очень удобной в целях клоакинга — это так называемая карта перезаписи. Это файлы, содержащие пары ключ / значение, обычно в формате текстового файла:

cde2c920.infoseek.com spider
205.226.201.32 spider
cde2c923.infoseek.com spider
205.226.201.35 spider
cde2c981.infoseek.com spider
205.226.201.129 spider
cde2cb23.infoseek.com spider
205.226.203.35 spider

Ключи, как вы видите, имена хостов или IP-адреса. В этом простеньком примере значение всегда одно — «spider». Естественно, в реальном файле значения будут другие. Эта директива может быть записана во второй («Конфигурация основного сервера») или третий («Виртуальные сервера»)
раздел файла httpd.conf:

RewriteMap botBase txt:/www/yourdomain/spiderspy.txt</code>
 
Карта перезаписи возымеет эффект на весь сервер. Также, в файл .htaccess записывается:
 
<pre lang="ini">RewriteCond ${botBase:%{REMOTE_HOST}} =spider [OR]
RewriteCond ${botBase:%{REMOTE_ADDR}} =spider
RewriteRule ^(.*).htm$ $1.htm [L]
RewriteRule ^.*.htm$ index.html [L]</code>
 
Данные условия будут производить системную проверку: произведен ли запрос поисковиком. С этой целью производится поиск по файлу spiderspy.txt. Если ключ найден, будет возвращено значение «spider», а «условие» будет являться истинным.Затем выполняется первый RewriteRule. Это означает то, что запрашиваемая '.htm' страница будет отдана поисковику. Переменная $1 равна части в круглых скобках '^(. *).htm$', то есть имя файла останется тем же самым.
 
Если же URL вызван обычным посетителем, то применяется второе «правило»: пользователь будет перенаправлен на страницу «index.html».
 
Поскольку «.htm» страницы будут читаться только «пауками», они могут быть оптимизированы соответственно для поисковых серверов. Вы можете также использовать файл в формате «dbm»вместо обычного текстового файла. Бинарный формат данных позволяет ускорить поиск, который
является особенно важным, если вы работаете с очень большими списками поисковиков. Пример, данный выше, предлагает простые функциональные возможности клоакинга. Все обычные посетители будут всегда переадресовываться к странице index.html и не будет вестись никаких
логов файлов вне логов mod_rewrite.
 
Можно заменить несколько строчек кода php (perl и т.д.) в ваших приложениях, используя всего одну-две строки mod_rewrite. Последний пример проиллюстрирует это более подробно.
 
Цель — показать посетителям 'фото дня'. Посетитель, кликнувший по ссылке http://yoursite.com/pic.html увидит лучшую фотографию или картинку дня, и так каждый день. Мы будем работать с серверными переменными:
 
<pre lang="ini">TIME_MON
TIME_DAY</code>
 
Поместим в файл .htaccess одну единственную строку:
 
<pre lang="ini">RewriteRule ^pic.html$ pic-%{TIME_MON}-%{TIME_DAY}.html</code>
 
Запрашиваемый URL будет перезаписан, например:
 
<pre lang="ini">pic-08-28.html
pic-08-29.html
pic-08-30.html
и так далее.

Теперь, все что вы должны сделать — это единожды загрузить файлы с соответсвующими именами и забыть о ежедневном обновлении ссылки. Переменные времени также могут использоваться для другой периодичности.

Преобразование ссылок с помощью Apache (mod_rewrite)

Допустим, у вас есть работающий веб-сайт, состоящий из большого количества статических html-страниц. Все идет хорошо, но вдруг в какой-то момент вы решаете усовершенствовать работу веб-сайта и добавляете динамические скрипты: в результате страничка новостей теперь доступна по ссылке http://www.site.com/cgi-bin/news.cgi вместо прежней http://www.site.com/news.html, а каталог, в котором хранились страницы с описанием российских регионов, полностью перекочевал в динамику, и наш горячо любимый 77-й регион теперь доступен по неэстетично выглядящей ссылке http://site.ru/cgi-bin/regions.pl?region=77&mode=brief вместо легко запоминаемой http://site.ru/regions/77.html.

Подобные изменения требуют замены соответствующих ссылок на всех страницах вашего веб-сайта, которые ссылаются на новости и регионы, но это еще цветочки. Основная проблема заключается в том, что на эти страницы могут ссылаться другие веб-сайты, о существовании которых вы даже не подозреваете. Да и посетители вашего веб-сайта могли создать соответствующие закладки в своих браузерах, и поэтому они будут неприятно удивлены, когда вместо странички новостей получат ошибку «404: страница не найдена».

Анализ проблемы наводит на мысль о том, как хорошо было бы иметь возможность обращаться к одним и тем же страницам по различным HTTP-ссылкам, причем для страниц с похожими ссылками было бы очень удобно описать одно общее для них правило вместо того, чтобы для каждой страницы выписывать все возможные варианты ведущих на нее HTTP-ссылок. Если ваш веб-сайт работает под управлением весьма популярного в настоящее время веб-сервера Apache (скорее всего, это именно так), то в вашем распоряжении есть мощнейшее средство преобразования ссылок, которое реализуется специальным программным модулем mod_rewrite. Некоторые директивы данного модуля могут использоваться только в конфигурационном файле самого веб-сервера, другие же — в специальных файлах .htaccess, которые можно располагать в подкаталогах иерархии вашего веб-сайта. Именно эти директивы из .htaccess и производят основную работу по преобразованию ссылок, поэтому их мы опишем более подробно.

Для перестраховки можно уточнить у службы технической поддержки вашего хостера, включен ли модуль mod_rewrite в состав веб-сервера, обслуживающего ваш веб-сайт, и допускается ли использование его директив в файлах .htaccess. Поскольку данный модуль широко используется во многих проектах, каждый уважающий себя хостер на оба ваших вопроса ответит: «Да, конечно». Если же вы получили отрицательный ответ, это хороший повод задуматься о смене хостинг-провайдера на другого, предоставляющего более качественные услуги.

Прежде чем углубиться в описание возможностей модуля mod_rewrite, приведем пример решения двух описанных выше проблем:

# Включение преобразования ссылок
RewriteEngine on
# Новостная страница
RewriteRule ^news.html$ /cgi-bin/news.cgi
# Страницы с описаниями регионов
RewriteRule ^regions/([0-9]+)\.html$ /cgi-bin/regions.pl?region=$1&mode=brief

Директива RewriteEngine включает или выключает преобразование ссылок (соответственно «RewriteEngine on» или «RewriteEngine off»). Действие директивы распространяется на текущий каталог и на все его подкаталоги, в которых нет своих файлов .htaccess с данной директивой.

Правила преобразования ссылок наследуются чуть сложнее. Чаще всего преобразование по умолчанию отключено в основном конфигурационном файле веб-сервера. Допустим, что вы записали в .htaccess некоего каталога директиву «RewriteEngine on» и некоторое количество правил преобразования. Перейдем теперь в один из подкаталогов. Если здесь нет файла .htaccess, либо в нем нет ни одной директивы модуля mod_rewrite, то все правила преобразования наследуются от родительского каталога. Если в файле .htaccess есть хотя бы одна директива модуля mod_rewrite, то не наследуется ничего, а состояние по умолчанию выставляется таким же, как в главном конфигурационном файле веб-сервера (по умолчанию «off»). Поэтому, если вы желаете иметь в этом каталоге свой набор правил преобразования, не забудьте добавить директиву «RewriteEngine on». Есть и третий вариант. Допустим, вы желаете унаследовать все правила из родительского каталога и добавить к ним несколько новых — для этого вам понадобится директива RewriteOptions, которая допускает только один фиксированный аргумент. Таким образом, в файл .htaccess вы должны записать ваши новые правила и две директивы: «RewriteEngine on» и «RewriteOptions inherit».

А теперь перейдем непосредственно к описанию правил. Преобразования описываются при помощи директивы RewriteRule. Правил может быть несколько, при этом все они применяются в порядке их описания. Когда правила заканчиваются, они вновь начинают применяться с самого начала, и этот цикл продолжается до тех пор, пока «срабатывает» хотя бы одно из правил. В некоторых случаях это может приводить к зацикливанию, поэтому при описании правил нужно быть предельно внимательным. Существует несколько специальных флагов, которые предоставляют возможность прервать этот процесс на определенном правиле или пропустить несколько правил (об этом будет рассказано ниже). Синтаксис директивы RewriteRule выглядит следующим образом:

RewriteRule «исходный путь» «замена» «флаги»

Исходный путь — это часть исходной ссылки, от которой отрезаны имя сервера, путь до текущего каталога и параметры запроса. Допустим, что ваш веб-сайт www.site.com расположен в каталоге /home/site/www. Тогда для ссылки http://www.site.com/test/list.html?mode=0 исходным путем в каталоге /home/site/www будет test/list.html, а в каталоге /home/site/www/test — list.html. Исходный путь задается регулярным выражением. Символ ! перед исходным путем означает, что правило «срабатывает» по несовпадению ссылки с заданным регулярным выражением.

Замена — это то, на что будет заменена исходная ссылка в случае «срабатывания» правила. Замена может быть относительной (если она не начинается с символа /) и абсолютной (если она начинается с символа / или представляет собой полную ссылку, начинающуюся с http:// или https://). В замене можно использовать определенные части исходного пути, отмеченные круглыми скобками. При этом макрос $1 обозначает ту часть исходного пути, которая расположена внутри первой пары скобок, $2 — внутри второй пары и так далее.

Флаги — это дополнительные опции для данного правила, которые перечисляются в квадратных скобках через запятую.

  • R (redirect) останавливает процесс преобразования и возвращает результат браузеру клиента как редирект на данную страницу (302, MOVED TEMPORARY). С данным флагом можно указать другой код результата, например «R=301» возвратит редирект с кодом 301 (MOVED PERMANENTLY).
  • F (forbidden) возвращает ошибку 403 (FORBIDDEN).
  • G (gone) возвращает ошибку 410 (GONE).
  • P (proxy) — по этому флагу Apache выполняет подзапрос (sub-request) к указанной странице с использованием программного модуля mod_proxy, при этом пользователь ничего не узнает об этом подзапросе. Если модуль mod_proxy не входит в состав вашей сборки Apache, то применение данного флага вызовет ошибку.
  • L (last) останавливает процесс преобразования, и текущая ссылка считается окончательной.
  • N (next) запускает процесс преобразования с первого по порядку правила.
  • C (chain) объединяет несколько правил в цепочку. Если первое правило цепочки «не срабатывает», то вся цепочка игнорируется.
  • NS (nosubreq) разрешает «срабатывание» правила только для настоящих запросов, игнорируя подзапросы (подзапрос может быть вызван, например, включением файла при помощи директивы SSI).
  • NC (nocase) отключает проверку регистра символов.
  • QSA (qsappend) добавляет исходные параметры запроса (query string) к замене. Если замена не включает в себя новые параметры запроса, то исходные параметры запроса добавляются автоматически. Если же включает, то без флага QSA исходные параметры запроса будут утеряны.
  • PT (passthrough) останавливает процесс преобразования и передает полученную новую ссылку дальше «по цепочке», чтобы над ней могли «поработать» директивы Alias, ScriptAlias, Redirect и им подобные (тогда как при флаге L новая ссылка считается окончательной и не подлежит дальнейшей обработке).
  • S (skip) пропускает следующее правило, если данное правило «сработало». Можно пропускать несколько правил, если указать их количество, например: «S=3».
  • E (env) устанавливает переменную окружения, например: «E=переменная:значение».

Примеры (во всех случаях показано содержимое файла .htaccess, расположенного в корневом каталоге веб-сайта):

# Пример 1. Каталоги проектов project1 и project2 веб-сайта www.site.com ранее содержали статические 
# html-страницы, теперь же эти страницы расположены на двух отдельных веб-сайтах project1.ru и project2.ru 
# (в той же иерархии)
 
# Первый способ требует наличия модуля mod_proxy и создает дополнительную нагрузку на веб-сервер, но зато 
# посетитель веб-сайта не знает, откуда в действительности выбираются веб-страницы
 
# Символы / даются с вопросительными знаками, чтобы правильно обработать ссылки вида
# http://www.site.com/project1 и http://www.site.com/project1/
 
RewriteRule ^project1/?(.*) http://project1.ru/$1 [P]
RewriteRule ^project2/?(.*) http://project2.ru/$1 [P]
 
# Второй способ возвращает внешние редиректы, так что посетитель увидит в адресной строке своего браузера, 
# что страницы реально расположены на других веб-сайтах
 
RewriteRule ^project1/?(.*) http://project1.ru/$1 [R]
RewriteRule ^project2/?(.*) http://project2.ru/$1 [R]
 
# Допустим, что в редиректах мы желаем передать в запросе какие-нибудь дополнительные параметры. 
# Применение флага QSA позволит нам сохранить параметры оригинального запроса, так что ссылка 
# http://site.com/project1/news.pl?mode=daily будет преобразована в
# http://project1.ru/news.pl?came_from=site.comamode=daily
 
RewriteRule ^project1/?(.*) http://project1.ru/$1?came_from=site.com [R,QSA]
RewriteRule ^project2/?(.*) http://project2.ru/$1?came_from=site.com [R,QSA]
# Пример 2. Электронная книга отдается динамическим скриптом в то время как нам желательно иметь 
# "красивую" иерархию вида "http://lib.ru/book1/chapter3.html". Кстати, расширение .html помогает нам скрывать 
# динамическую природу нашего веб-сайта
 
RewriteRule ^([a-z0-9]+)/([a-z0-9]+)\.html$ /cgi-bin/view_chapter.cgi?book=$1&amp;chapter=$2 [NC]
# Пример 3. Нам желательно скрыть от пользователя используемую на веб-сайте технологию, для чего мы не будем 
# пользоваться расширениями в наших http-ссылках. Без флага L данное правило зациклится
 
RewriteRule (.+) $1.html [L]
 
# В то же время посетитель может ввести ссылку с расширением по одному ему понятным причинам. Правильно 
# обработать такую ситуацию поможет следующее правило:
 
RewriteRule ^([^.]+) $1.html [L]
# Пример 4. На веб-сайте есть статические ссылки с расширением .html и динамические ссылки с расширением .pl. 
Допустим, что динамические ссылки остались прежними, а статические должны обрабатываться cgi-скриптом
# Первый вариант предельно прост:
 
RewriteRule (.+)\.html$ /cgi-bin/new_script.cgi?page=$1 [L]
 
# Второй вариант более общий. Например, если нам нужно преобразовать массу различных ссылок кроме одной-двух, 
# можно воспользоваться специальной "заменой без изменения" (обозначается символом -):
 
RewriteRule \.pl$ - [L]
RewriteRule (.*) /cgi-bin/new_script.cgi?page=$1 [L]
# Пример 5. Есть один особый случай, когда делается внешний редирект на относительную ссылку. Допустим, мы 
# находимся в каталоге /home/site.com/www/test веб-сайта site.com. Каталог доступен по ссылке http://site.com/test/. 
# Нам нужен внешний редирект с файлов *.html на *.shtml. Приводимые директивы записываются 
# в файл /home/site.com/www/test/.htaccess
 
# Решение тривиально, если использовать абсолютную замену, но в этом случае нам приходится жестко прописывать 
# название каталога, что не совсем хорошо:
 
RewriteRule (.+)\.html /test/$1.shtml [R]
 
# Если написать замену как относительную ссылку (см. ниже), то результат будет не таким, каким мы его ожидаем 
# увидеть (это обусловлено особенностями преобразования ссылок на уровне каталогов): например, ссылка 
# http://site.com/test/aaa.html будет преобразована в http://site.com/home/site.com/www/test/aaa.shtml
 
RewriteRule (.+)\.html $1.shtml [R]
 
# По полученной ссылке видно, что там подставлен полный реальный путь к нужному файлу. Решить проблему можно 
# при помощи директивы RewriteBase, параметром которой является префикс для всех относительных замен, 
# находящихся в данном файле .htaccess
 
RewriteBase /test
# Пример 6. Задание переменных окружения применяется очень редко, но тем не менее приведем два примера, 
# не нуждающихся в пояснении
 
# Сохраняет в окружении расширение исходного файла
 
RewriteRule ^([^.]+)\.([a-z]+)$ /cgi-bin/new_script.cgi?page=$1 [L,E=EXT:$2]
 
# Сохраняет в окружении содержимое http-заголовка X-Forwarded-For
 
RewriteRule \.(cgi|pl)$ - [L,E=%{HTTP:X-Forwarded-For}]

Несмотря на такое изобилие, преобразование ссылок не ограничивается только директивой RewriteRule. Есть еще одна директива, которая используется не менее часто — это директива RewriteCond. Данная директива предназначена для проверки некоторых дополнительных параметров и всегда ставится непосредственно перед директивой RewriteRule. Если директива RewriteCond «срабатывает», то проверяется следующая за ней директива RewriteRule, если же «не срабатывает», то директива RewriteRule игнорируется.

# Если подряд записаны несколько директив RewriteCond, то следующая за ними директива RewriteRule 
# проверяется только в том случае, когда "сработали" все директивы RewriteCond:
 
RewriteCond условие1
RewriteCond условие2
RewriteRule преобразование1
RewriteRule преобразование2
 
# Следует обратить внимание, что в приведенном выше примере вторая директива RewriteRule проверяется в любом 
# случае, так как все директивы RewriteCond относятся только к первой директиве RewriteRule. Если же вы желаете, 
# чтобы условия относились к обеим директивам RewriteRule, то вам придется повторить их еще раз:
 
RewriteCond условие1
RewriteCond условие2
RewriteRule преобразование1
RewriteCond условие1
RewriteCond условие2
RewriteRule преобразование2
 
# Применение флага OR позволяет объединять условия не по И (как это делается по умолчанию), а по ИЛИ. В 
# следующем примере директива RewriteRule проверяется, если выполняется любое из двух предшествующих условий:
 
RewriteCond условие1 [OR]
RewriteCond условие2
RewriteRule преобразование
 
Синтаксис директивы RewriteCond выглядит следующим образом:
 
RewriteCond «проверяемое выражение» «условие» «флаги»

Проверяемое выражение — это строка, которая может состоять из обычных символов, макросов и переменных. Макросы $1, $2 и так далее ссылаются на соответствующие выражения в скобках из следующей по порядку директивы RewriteRule. Макросы %1, %2 и так далее ссылаются на выражения в скобках из предыдущей по порядку директивы RewriteCond. Кстати, макросы %* могут также использоваться и в директивах RewriteRule для ссылки на предыдущую директиву RewriteCond.

Переменные записываются в виде %{ИМЯ_ПЕРЕМЕННОЙ}. Наиболее часто используются следующие переменные:

  • QUERY_STRING (параметры запроса),
  • REMOTE_ADDR (IP-адрес посетителя),
  • REMOTE_HOST (имя хоста посетителя),
  • REMOTE_USER (имя пользователя, если он прошел авторизацию),
  • REMOTE_METHOD (обычно GET или POST),
  • PATH_INFO (путь к файлу веб-страницы),
  • HTTP_USER_AGENT (содержимое http-заголовка User-Agent),
  • HTTP_REFERER (содержимое http-заголовка Referer),
  • HTTP_COOKIE (содержимое http-заголовка Cookie),
  • HTTP_HOST (имя хоста веб-сайта),
  • TIME_YEAR (все переменные TIME_* хранят разбитые на части текущие дату и время), TIME_MON, TIME_DAY, TIME_HOUR, TIME_MIN, TIME_SEC, TIME_WDAY,
  • REQUEST_URI (строка запроса без имени хоста и параметров запроса),
  • REQUEST_FILENAME (имя файла из REQUEST_URI),
  • THE_REQUEST (полная строка запроса в том виде, в котором ее присылает браузер посетителя).
  • Помимо стандартных переменных можно проверять содержимое любого http-заголовка: %{HTTP:Название-Заголовка}.

Условие — это обычное регулярное выражение. Кроме регулярных выражений существует еще несколько видов условий (условию может предшествовать символ !, который трактуется как отрицание):

  • =ABC — значение переменной должно быть лексически равно строке ABC;
  • >ABC — значение переменной должно быть лексически больше строки ABC;
  • <ABC — значение переменной должно быть лексически меньше строки ABC;
  • -d — должен существовать каталог, имя которого совпадает со значением переменной;
  • -f — должен существовать файл, имя которого совпадает со значением переменной;
  • -s — должен существовать файл ненулевой длины, имя которого совпадает со значением переменной;
  • -l — должен существовать симлинк, имя которого совпадает со значением переменной;
  • -F— должен существовать файл, имя которого совпадает со значением переменной, и этот файл должен быть доступен по внешней ссылке на данный веб-сайт;
  • -U — должна быть доступна http-ссылка, имя которой совпадает со значением переменной.

Флагов может быть всего два: OR (объединение директив RewriteCond по ИЛИ, как было написано выше) и NC (отключение проверки регистра аналогично одноименному флагу для директивы RewriteRule).

И, наконец, примеры применения:

# Пример 1. Жесткий запрет посещений нашего веб-сайта для робота поисковой системы Google
 
RewriteCond %{USER_AGENT} Googlebot
RewriteRule .* - [F]
 
# Другой вариант возвращает вместо ошибки 403 (FORBIDDEN) ошибку 404 (NOT_FOUND)
 
RewriteCond %{USER_AGENT} Googlebot
RewriteRule .* - [R=404]
# Пример 2. Есть два каталога /home/site/storage1 и /home/site/storage2, в которых нужно искать запрашиваемые файлы
 
RewriteCond /home/site/storage1/%{REQUEST_FILENAME} -f
RewriteRule (.+) /home/site/storage1/$1 [L]
RewriteCond /home/site/storage2/%{REQUEST_FILENAME} -f
RewriteRule (.+) /home/site/storage2/$1 [L]
# Пример 3. Если ссылка не найдена на нашем веб-сайте, отправить посетителя на www.site.ru
 
RewriteCond %{REQUEST_URI} !-U
RewriteRule (.*) http://www.site.ru/$1 [R]
# Пример 4. Закрыть доступ к веб-сайту в рабочее время
 
RewriteCond %{TIME_HOUR}%{TIME_MIN} >1000
RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900
RewriteRule .* - [F]
# Пример 5. Есть скрипт, раз в сутки производящий оптимизацию базы данных. Если посетитель зайдет в
 этот момент на веб-сайт, то получит ошибку - вместо этого необходимо показать ему страницу с сообщением о том, 
что через пару минут все придет в норму. Оптимизирующий скрипт на время своей работы создает файл /home/site/optimizer.pid
 
RewriteCond /home/site/optimizer.pid -f
RewriteRule .* /optimization_message.html
# Пример 6. Посетители веб-сайта авторизуются при помощи стандартной авторизации (AuthType BasicAuth).
# Необходимо по ссылке /home/* показывать содержимое их домашних каталогов
 
RewriteCond %{REMOTE_USER} !=""
RewriteCond /home/(%{REMOTE_USER}) -d
RewriteRule (.*) /home/%1/$1

И в заключение вкратце упомянем еще о двух директивах преобразования ссылок, которые не входят в модуль mod_rewrite — это Redirect и RedirectMatch. Ими можно пользоваться, во-первых, как упрощенными вариантами директивы RewriteRule, а во-вторых, в случаях, когда модуль mod_rewrite по каким-то причинам отсутствует в вашей сборке веб-сервера Apache. Примеры:

# Обе директивы при «срабатывании» возвращают редиректы. 
# В качестве замены всегда должна использоваться абсолютная ссылка
 
# Обычный безусловный редирект:
Redirect /news http://www.site.com/cgi-bin/news.cgi
 
# Редирект с подстановкой:
RedirectMatch (.*\.gif)$ http://www.site.com/alt/path/to/gif/files$1

Возможно, на первый взгляд преобразование http-ссылок с помощью модуля mod_rewrite покажется очень сложной задачей, но на самом деле это не так: с опытом придет и понимание, и мастерство. Если вы внимательно читаете документацию, четко представляете необходимые преобразования ссылок и тщательно проверяете написанные вами правила, все будет работать правильно. Иначе и быть не может, не так ли?

Пошаговая настройка SSL для Apache

Перевод статьи «Step by Step: Configuring SSL Under Apache» автора Juliet Kemp.

Вступление

Если у вас безопасный веб-сервер, пользователи, волнующиеся за безопасность своих данных, могут быть уверены, что запросы зашифрованы, поэтому их данные в безопасности. Лучшим путем для этого является использование Apache 2, лидирующего веб-сервера под Linux и Secure Sockets Layer, протокол безопасной передачи данных. Transport Layer Security (TLS) является развитием SSL, но они работают практически одинаково. Я буду ссылаться только на SSL.

SSL — протокол для безопасной передачи зашифрованных данных между веб-браузером и веб-сервером. В большинстве случаев, аутентификацию проходит сервер, что позволяет клиенту быть уверенным в том, что сервер является требуемым ему, а не наоборот. Как бы то ни было, когда соединение устанавливается, обе стороны находятся в безопасности, так как только клиент и сервер имеют доступ к ключу. Это работает в течении многих запросов, сервер не интересует, кем является клиент так долго, сколько он остается тем же клиентом на протяжении запроса. Если вас волнует аутентификация клиента, есть возможность использовать клиентские SSL сертификаты (или htaccess, Kerberos или другие, близкие к ним методы), но это не будет рассматриваться в этой статье.

Будучи на стороне клиента, вас волнует, тому ли человеку (серверу) вы посылаете какие-либо личные данные, которые хотите зашифровать. Поэтому сервер, а не клиент, аутентифицируется. Вас также беспокоит участие третей стороны в доступе к вашим данным в том виде, каком вы их посылаете. SSL предоставляет оба этих вида безопасности.

Протокол SSL работает следующим образом:

1. Клиент подключается к веб-серверу и дает список доступных кодов.

2. Сервер берет наиболее устойчивый код, который поддерживает и он, и клиент, и посылает сертификат со своим именем и ключ кодирования, подписанный доверенным Удостоверяющим Центром (Certificate Authority, далее — CA), таким как Verisign.

3. Клиент проверяет сертификат с помощью CA. На практике, хранят набор CA локально, поэтому это может быть сделано без контакта в реальном времени с CA, и поэтому более быстро.

4. Клиент посылает назад случайное число, зашифрованное с помощью публичного ключа сервера. Только клиент знает это число, и только сервер может его расшифровать (используя личный ключ); вот где реализуется безопасность от участия третей стороны.

5. Сервер и клиент используют случайное число для генерирования содержимого ключа, который используется на протяжении передачи данных.

Мы хотим сделать это на стороне клиента настолько прозрачным, насколько это возможно, чтобы сделать передачу данных настолько легкой, насколько возможно.

Настройка Apache для работы с SSL проста, но есть несколько необходимых шагов. Эта статья рассказывает, как получить сертификат, подписанный CA и как скомпилировать и настроить Apache для работы с SSL. Я использую Apache 2 с mod_ssl. ApacheSSL (реализация Apache с возможностями SSL) также доступен, но сейчас он уже устарел; mod_ssl намного лучше.

Создание сертификата.

Первый шаг — создание сертификата. Вы можете создать сертификат с паролем или без него. Главным неудобством использования пароля является тот факт, что он должен быть введен каждый раз при старте сервера. Поэтому он не может запускаться без присмотра или автоматически при загрузке, например, после выключения электричества. В зависимости от ваших установок, это может быть значимым фактом для вас, или нет.

Теоретически, преимуществом использования пароля являтся повышение защиты. Хотя, на практике пароли не дает такой большой защиты. Если кто-то сможет прочитать или скопировать приватный ключ, значит, у него уже есть доступ к системе и возможность получить пароль, например, используя такую программу, как кейлоггер. Пароль защитит от скрипт-кидди, но не от серьезного взломщика. Возможно, для большинства людей нет смысла его использовать.

Для тестовых целей, или для небольшой локальной сети, вы можете создать сертификат, подписанный вами. Вы можете сделать это, выполнив команду:

openssl req -new -x509 -days 365 -sha1 -newkey rsa:1024 \
 
-nodes -keyout server.key -out server.crt \
 
-subj '/O=Company/OU=Department/CN=www.example.com'

Давайте рассмотрим опции более подробно:

— -x509 означает, что сертификат обязателен, правильнее, чем просто запрос сертификата (смотри ниже)

— -days 365 устанавливает время истечения сертификата, равное году. Вы можете увеличить этот срок. Запомните дату истечения срока, чтобы обновить её при необходимости.

— -sha1 указывает, что будет использован SHA1 для шифрования.

— rsa:1024 делает ключ 1024 битным RSA.

— -nodes указывает отсутствие пароля.

— -keyout и -out указывают, где хранить сертификат и ключ. Ключ должен быть доступен для чтения только для root; сертификат может быть доступен для чтения для world, но должен быть доступным для чтения пользователю, который запускает Apache.

— -subj устанавливает имя компании, имя отделения компании и адрес веб-сайта. Если вы это пропустите, вас попросят это сделать. CN должен совпадать с адресом сайта, иначе сертификат не будет соответствовать, и пользователи будут получать предупреждение при подключении. Убедитесь, что вы не запрашиваете пароль.

Проблема с использованием сертификата, подписанного самостоятельно для сервера, работающего в реальном времени, заключается в том, что любой браузер, работающий с сайтом, не будет признавать сертификат правильным. Это значит, что пользователя будут запрашивать о подтверждении сертификата. Очевидно, что в большинстве случаев это не является оптимальным решением. Хотя, это нормально для целей теста, а для небольших локальных сетей было бы плохим вариантом платить за сертификат от CA.

Не смотря на это, безусловно, лучше использовать сертификат, подписанный доверенным Удостоверяющим Центром, таким как Verisign (который имеет наибольший охват рынка), или меньшей организацией. Большинство браузеров уже имеют набор предустановленных сертификатов, заверенных CA, которые верифицируют сертификат вашего веб-сервера при подключении клиента. Это уменьшает количество трудностей для конечного пользователя и удостоверяет законность вашего сайта.

Чтобы получить сертификат, подписанный CA, прежде всего вы должны создать криптографическую пару и запрос сертификата:

openssl req -new -sha1 -newkey rsa:1024 -nodes \ 
 
-keyout server.key -out www.example.com.csr \ 
 
-subj '/O=Company/OU=Department/CN=www.example.com'

Этот пример работает так же, как и предыдущий, но на этот раз мы не используем опцию -x509. Выполнение этой команды приведет к генерации ключа и запроса сертификата, но не самого сертификата. Если вы ввели CN и так далее, вы не должны вводить адрес e-mail или пароль.

Ключ сервера (файл server.key, который, опять же, должен быть доступен для чтения только для root) остается на вашем веб-сервере; запрос (файл www.example.com.csr) отправляется в CA. Вы можете назвать файл запроса так, как вам вздумается, но назвав его по своему доменному имени, вы упростите задачу для CA.

Следующей стадией будет послать этот файл www.example.com.csr в CA, с вашей оплатой. Они должны вернуть его достаточно быстро, если вы предоставили всю требуемую информацию в вашем запросе. Выбранный вами CA объяснит их действия на своем сайте. Вам может понадобиться поменять формат файла на PEM, но, в случае Verisign, этого делать не придется.

Когда вы получите файл назад в PEM формате, переименуйте его в server.crt (это не является строгой необходимостью, но соответствует условиям Apache) и проверьте его:

    openssl verify -CAfile /path/to/trusted_ca.crt \
 
		-purpose sslserver server.crt

Затем проверьте, что результат выполнения этих двух команд одинаков, т.е., что сертификат соответствует приватному ключу:

    openssl x509 -noout -modulus -in server.pem | openssl sha1
 
    openssl rsa -noout -modulus -in server.key | openssl sha1

Теперь установите ваш ключ (сгенерированный выше как server.key) и сертификат (server.crt) в /etc/apache2/ssl или предпочитаемый вами каталог настроек, если он другой. Как указано выше, очень важно убедиться, что server.key доступен для чтения только для root, в то время как сертификат сервера может быть доступен для чтения для world, но принадлежать и быть доступным для записи только для root.

Компиляция Apache с SSL.

Итак, ваш сертификат сгенерирован. Теперь вам надо настроить свой сервер для его использования.

Для подавляющего большинства людей, лучшим способом установить и управлять Apache2 — модули, полученные через менеджер пакетов вашего дистрибутива. Apache2 из Debian идет вместе с модулем SSL, но он не включен по умолчанию. Для его включения вы должны выполнить команду: a2enmod ssl и перезапустить веб-сервер.

Чтобы сделать это, добавьте строчку

    Include /etc/apache2/mod_ssl.conf

в ваш /etc/apache2/apache2.conf (этот файл может также называться httpd.conf). Вы должны исправить её, обозначив соответствующее расположение файла mod_ssl.conf. Затем перезапустите веб-сервер.

Если вы хотите скомпилировать Apache2 из исходных кодов, в зависимости от того, какие установки вы до этого использовали, вы можете уже иметь или не иметь поддержку SSL. Проверьте это командой apache2 -l. Если перекомпиляция понадобится, запустите ./configure со всеми опциями, которые вы использовали до этого, добавив к ним —enable-ssl и —enable-setenvif (последняя нужна для совместимости с капризами Internet Explorer). Затем установите его как обычно, с помощью make;make install и проверьте правильность прав доступа.

Настройка Apache с SSL.

Следующим шагом будет настройка Apache2. Следующие инструкции приведут к запуску сервера как безопасного (порт 443) и как обычного веб-сервера (порт 80). Прежде всего, вам надо настроить сервер на принятие запросов на оба порта. Или отредактируйте /etc/apache2/ports.conf (в Debian, это входит в apache2.conf), или отредактируйте /etc/apache2/apache2.conf, включив строки:

Listen 80
 
Listen 443

Затем отредактируйте /etc/apache2/sites-enabled/yoursite для использования настроек SSL. Разделение настроек обычного и безопасного сервера с помощью VirtualHost — простейший способ из соображений условий эксплуатации. Любые настройки вне секций VirtualHost (например, установка ServerAdmin) будут применяться для обоих (и любых других) VirtualHost. Добавьте следующий текст в файл конфигурации:

# =================================================
 
# SSL/TLS settings
 
# =================================================
 
NameVirtualHost *:443
 
    DocumentRoot "/local/www/ssl_html"
 
    SSLEngine on
 
    SSLOptions +StrictRequire
 
        SSLRequireSSL
 
    SSLProtocol -all +TLSv1 +SSLv3
 
    SSLCipherSuite HIGH:MEDIUM:!aNULL:+SHA1:+MD5:+HIGH:+MEDIUM
 
    SSLRandomSeed startup file:/dev/urandom 1024
 
    SSLRandomSeed connect file:/dev/urandom 1024
 
    SSLSessionCache shm:/usr/local/apache2/logs/ssl_cache_shm
 
    SSLSessionCacheTimeout 600    
 
    SSLCertificateFile /etc/apache2/ssl/server.crt
 
    SSLCertificateKeyFile /etc/apache2/ssl/server.key
 
    SSLVerifyClient none
 
    SSLProxyEngine off
 
        AddType application/x-x509-ca-cert      .crt
 
        AddType application/x-pkcs7-crl         .crl
 
    SetEnvIf User-Agent ".*MSIE.*" \  
 
      nokeepalive ssl-unclean-shutdown \  
 
      downgrade-1.0 force-response-1.0

Несколько замечаний об этой конфигурации:

— SSLEngine должен быть включен, обозначая, что сервер использует SSL.

— DocumentRoot устанавливает корневой каталог виртуального хоста. Это значит, что вы можете отделять безопасное содержимое от обычного.

— SSLRequireSSL запрашивает использование SSL (на этом виртуальном сервере), то есть, пользователь не может подключиться к этому виртуальному хосту с помощью обычного HTTP-запроса. Вот зачем мы разделили безопасное и обычное содержимое.

— SSLProtocol отключает все протоколы, отличные от TLS v1.0 и SSL v3.0. Для современных браузеров все будет работать хорошо.

— SSLCipherSuite устанавливает использование только HIGH и MEDIUM шифров. SHA1 считается более безопасным, чем MD5, поэтому выбран он.

— SSLCertificateFile и SSLCertificateKeyFile указывают расположение файлов сертификата и ключа.

— SSLVerifyClient должна быть установлена как ‘none’, если не используется аутентификация примера.

Чтобы запускать обычный сервер на 80 порту, добавьте следующий текст в конфигурационный файл:

NameVirtualHost: *.80
 
    DocumentRoot "/local/www/html"

После сохранения отредактированного конфигурационного файла, перезапустите сервер. Если вы использовали пароль при генерации сертификата, вам понадобится ввести его при запросе.

Тестирование.

Создайте базовую страницу index.html в корневой директории вашего сервера, если у вас ещё нет содержимого там.

Затем направьте свой браузер на https://yoursite.com. Вы должны увидеть открытие SSL-соединения и загрузку страницы. Если вы используете сертификат, подписанный вами, ваш браузер даст предупреждение о том, что подлинность сервера не может быть проверена. Вы можете выбрать просмотр сертификата и принять его. Если вы используете внешний сертификат, все должно пройти без проблем.

Убедитесь, что не можете получить доступ к безопасному содержимому, используя http://. Если вы попробуете, вы должны получить сообщение об ошибке.

Устранение проблем.

Если это работает не так, как ожидалось, прежде всего, проверьте, что ваш сервер вообще запущен с помощью команды ps -a | grep apache. Если она не вернет результата, попробуйте перезапустить сервер и проверьте сообщения об ошибке в консоли.

Также проверьте, что права доступа к файлам сертификата и ключа установлены правильно (см. выше), также, как и права к тестовому HTML-файлу и директории, в которой он находится.

Затем, проверьте логи. Вы должны проверить как логи сервера, так и логи SSL, которые вы настроили в конфигурационном файле выше. Если вы не нашли там ничего полезного, попробуйте поменять значение LogLevel в файле конфигурации Apache2 на ‘debug’, перезапустите Apache2 и протестируйте снова. Это должно дать больше данных в логах.

Если вы запускаете веб-сервер на 80 порту, попробуйте запросить страницу с помощью http://, вместо https://, чтобы понять, заключается ли проблема в веб-сервере или в SSL-соединении. Учтите, что в приведенных выше установках, корневые каталоги веб-сервера разные для http:// и https://, так что вы не можете (или не должны!) получить доступ к тому же содержимому. Если ваша тестовая страница в корневом каталоге http:// работает, в то время, как тестовая страница в корневом каталоге https:// не работает, это поможет вам более точно указать на проблему.

Если проблема в SSL соединении, удобным инструментом будет s_client, который является диагностической утилитой для решения проблем в TLS/SSL-соединениях. Обычное его использование: /usr/bin/openssl s_client -connect localhost:443. Также существует множество других опций, которые вы можете узнать из документации. Если вы получили сообщения об ошибках, это должно вам помочь в определении проблемы.

Заключение.

Поздравляю! Теперь у вас есть рабочий безопасный сервер, с сертификатом, автоматически проверяемым большинством современных браузеров.