Назад | Перейти на главную страницу

Перенаправление, изменение URL-адресов или перенаправление HTTP на HTTPS в Apache - все, что вы когда-либо хотели знать о правилах Mod_Rewrite, но боялись спросить

Это Канонический вопрос о модуле Apache mod_rewrite.

Изменение URL-адреса запроса или перенаправление пользователей на URL-адрес, отличный от того, который они изначально запрашивали, выполняется с помощью mod_rewrite. Сюда входят такие вещи, как:

Все, что вы когда-либо хотели знать о правилах Mod_Rewrite, но боялись спросить!

Как я могу стать экспертом в написании правил mod_rewrite?

Место для проверки ваших правил

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

порядок синтаксиса mod_rewrite

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

RewriteCond предшествующий RewriteRule делает это ОДНО правило условным. Любые последующие правила RewriteRules будут обрабатываться так, как если бы они не подчинялись условным операторам.

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html        $/blog/$1.sf.html

В этом простом случае, если HTTP-реферер находится с serverfault.com, перенаправьте запросы блога на специальные страницы serverfault (мы просто особенные). Однако, если в приведенном выше блоке была дополнительная строка RewriteRule:

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html        $/blog/$1.sf.html
RewriteRule $/blog/(.*)\.jpg         $/blog/$1.sf.jpg

Все файлы .jpg перейдут на специальные страницы serverfault, а не только те, у которых есть реферер, указывающий, что он пришел отсюда. Ясно, что не в этом смысл написания этих правил. Это можно сделать с помощью нескольких правил RewriteCond:

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html        /blog/$1.sf.html
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.jpg         /blog/$1.sf.jpg

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

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

Более сложный RewriteRule содержит условия для обработки. Последняя скобка, (html|jpg) сообщает RewriteRule соответствие либо html или jpg, и представить совпавшую строку как $ 2 в перезаписанной строке. Это логически идентично предыдущему блоку, с двумя парами RewriteCond / RewriteRule, он просто делает это на двух строках вместо четырех.

Несколько строк RewriteCond неявно объединены операцией AND и могут быть объединены явным оператором OR. Для обработки рефереров как от ServerFault, так и от суперпользователя (явное ИЛИ):

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)    [OR]
RewriteCond %{HTTP_REFERER}                ^https?://superuser\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

Чтобы обслуживать страницы, указанные в ServerFault, с помощью браузеров Chrome (неявное И):

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteCond %{HTTP_USER_AGENT}             ^Mozilla.*Chrome.*$
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

RewriteBase также зависит от заказа, поскольку указывает, как следующие RewriteRule директивы обрабатывают их. Это очень полезно для файлов .htaccess. Если используется, это должна быть первая директива в разделе «RewriteEngine on» в файле .htaccess. Вот пример:

RewriteEngine On
RewriteBase /blog
RewriteCond %{HTTP_REFERER}           ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg)         $1.sf.$2

Это сообщает mod_rewrite, что этот конкретный URL-адрес, который он в настоящее время обрабатывает, был получен посредством http://example.com/blog/ вместо физического пути к каталогу (/ home / $ Username / public_html / blog) и обрабатывать его соответствующим образом. Из-за этого RewriteRule считает, что начало строки находится после "/ blog" в URL-адресе. Вот одно и то же, написанное двумя разными способами. Один с RewriteBase, другой без:

RewriteEngine On

##Example 1: No RewriteBase##
RewriteCond %{HTTP_REFERER}                                   ^https?://serverfault\.com(/|$)
RewriteRule /home/assdr/public_html/blog/(.*)\.(html|jpg)     $1.sf.$2

##Example 2: With RewriteBase##
RewriteBase /blog
RewriteCond %{HTTP_REFERER}           ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg)         $1.sf.$2

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


Синтаксис соответствия RewriteRule

Сам RewriteRule имеет сложный синтаксис для сопоставления строк. Я расскажу о флагах (таких как [PT]) в другом разделе. Поскольку системные администраторы учатся на примере чаще, чем читая справочная страница Приведу примеры и объясню, что они делают.

RewriteRule ^/blog/(.*)$    /newblog/$1

В .* конструкция соответствует любому одиночному символу (.) ноль или более раз (*). Заключение его в круглые скобки указывает ему предоставить строку, которая была сопоставлена ​​как переменная $ 1.

RewriteRule ^/blog/.*/(.*)$  /newblog/$1

В этом случае первый. * НЕ был заключен в скобки, поэтому не передается в перезаписываемую строку. Это правило удаляет уровень каталога на новом блог-сайте. (/blog/2009/sample.html становится /newblog/sample.html).

RewriteRule ^/blog/(2008|2009)/(.*)$   /newblog/$2

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

RewriteRule ^/blog/(2008|2009)/(.*)$   /newblog/$1/$2

В этом случае мы используем $ 1 в перезаписанной строке.

RewriteRule ^/blog/(20[0-9][0-9])/(.*)$   /newblog/$1/$2

В этом правиле используется специальный синтаксис скобок, определяющий символ ассортимент. [0-9] соответствует цифрам от 0 до 9. Это конкретное правило будет работать с годами с 2000 по 2099.

RewriteRule ^/blog/(20[0-9]{2})/(.*)$  /newblog/$1/$2

Это делает то же самое, что и предыдущее правило, но часть {2} сообщает ему, что он должен соответствовать предыдущему символу (в данном случае выражению в скобках) два раза.

RewriteRule ^/blog/([0-9]{4})/([a-z]*)\.html   /newblog/$1/$2.shtml

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

RewriteRule ^/blog/([0-9]{4})/([-a-z]*)\.html  /newblog/$1/$2.shtml

Это перехватывает имена файлов с тире. Однако, как - - специальный символ в выражениях в квадратных скобках, это должен быть первый символ в выражении.

RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html   /newblog/$1/$2.shtml

Эта версия перехватывает любое имя файла с буквами, цифрами или - символ в имени файла. Вот как вы указываете несколько наборов символов в выражении в скобках.


Флаги RewriteRule

Флаги правил перезаписи имеют множество специальных значений и вариантов использования..

RewriteRule ^/blog/([0-9]{4})/([-a-z]*).\html  /newblog/$1/$2.shtml  [L]

Флаг - это [L] в конце приведенного выше выражения. Можно использовать несколько флагов, разделенных запятой. Связанная документация описывает каждый из них, но они все равно здесь:

L = Последний. Остановите обработку RewriteRules, как только это совпадет. Количество заказов!
C = Цепь. Продолжить обработку следующего RewriteRule. Если это правило не совпадает, следующее правило выполняться не будет. Подробнее об этом позже.
E = Установить переменную окружения. Apache имеет различные переменные среды, которые могут влиять на поведение веб-сервера.
F = Запрещено. Возвращает ошибку 403-Forbidden, если это правило соответствует.
грамм = Ушел. Если это правило соответствует, возвращает ошибку 410-Gone.
ЧАС = Обработчик. Принудительно обрабатывает запрос, как если бы он был заданным MIME-типом.
N = Далее. Заставляет правило запускаться заново и повторно соответствовать. БЫТЬ ОСТОРОЖЕН! Могут возникнуть петли.
NC = Нет дела. Позволяет jpg чтобы соответствовать как jpg, так и JPG.
NE = Выхода нет. Предотвращает перезапись специальных символов (.? # И т. Д.) В их эквиваленты в шестнадцатеричном коде.
NS = Нет подзапросов. Если вы используете включения на стороне сервера, это предотвратит совпадение с включенными файлами.
п = Прокси. Заставляет обрабатывать правило mod_proxy. Прозрачно предоставляйте контент с других серверов, потому что ваш веб-сервер получает его и повторно обслуживает. Это опасный флаг, так как плохо написанный флаг превратит ваш веб-сервер в открытый прокси, и это плохо.
PT = Проходить. Учитывайте операторы Alias ​​при сопоставлении RewriteRule.
QSA = QSAppend. Если исходная строка содержит запрос (http://example.com/thing?asp=foo) добавить исходную строку запроса к перезаписанной строке. Обычно его выбрасывают. Важно для динамического контента.
р = Перенаправить. Обеспечьте перенаправление HTTP на указанный URL. Также может предоставить точный код перенаправления [R = 303]. Очень похоже на RedirectMatch, который работает быстрее и его следует использовать по возможности.
S = Пропустить. Пропустите это правило.
Т = Тип. Укажите mime-тип возвращаемого содержимого. Очень похоже на AddType директива.

Вы знаете, как я это сказал RewriteCond относится к одному и только одному правилу? Что ж, вы можете обойти это, создав цепочку.

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html        /blog/$1.sf.html     [C]
RewriteRule ^/blog/(.*)\.jpg         /blog/$1.sf.jpg

Поскольку первое RewriteRule имеет флаг цепочки, второе правило перезаписи будет выполняться, когда будет выполнено первое, то есть когда предыдущее правило RewriteCond будет сопоставлено. Удобно, если регулярные выражения Apache причиняют боль вашему мозгу. Однако метод «все в одной строке», на который я указываю в первом разделе, быстрее с точки зрения оптимизации.

RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html   /newblog/$1/$2.shtml

Это можно сделать проще с помощью флагов:

RewriteRule ^/blog/([0-9]{4})/([-0-9a-z]*)\.html   /newblog/$1/$2.shtml   [NC]

Также некоторые флаги применимы и к RewriteCond. В частности, NoCase.

RewriteCond %{HTTP_REFERER}        ^https?://serverfault\.com(/|$)     [NC]

Будет соответствовать "ServerFault.com"

Каков основной формат и структура правил mod_rewrite?

Я полагаюсь на отличный ответ sysadmin1138 по этим вопросам.

Какую форму / вид регулярных выражений мне нужно хорошо понимать?

В дополнение к порядку синтаксиса, соответствию синтаксиса / регулярным выражениям и флагам RewriteRule, описанным sysadmin1138, я считаю, что стоит упомянуть, что mod_rewrite предоставляет переменные среды Apache на основе заголовков HTTP-запросов и конфигурации Apache.

я бы порекомендовал Учебное пособие по отладке mod_rewrite от AskApache для получения полного списка переменных, которые могут быть доступны для mod_rewrite.

Каковы наиболее частые ошибки / подводные камни при написании правил перезаписи?

Большинство проблем с RewriteRule возникают из-за неправильного понимания синтаксиса PCRE / невозможности правильно экранировать специальные символы или отсутствия понимания содержимого переменных, используемых для сопоставления.

Типичные проблемы и рекомендуемые способы их устранения:

  • внутренняя ошибка сервера 500 - Удалить элементы управления кареткой Windows в конфигурационных файлах, если они есть, убедитесь, что включен mod_rewrite (директивы переноса в IfModule условно, чтобы избежать этого сценария), проверьте синтаксис директив, закомментируйте директивы, пока проблема не будет выявлена
  • Цикл перенаправления - Используйте RewriteLog и RewriteLogLevel, закомментируйте директивы, пока проблема не будет выявлена.

Каков хороший метод тестирования и проверки правил mod_rewrite?

Во-первых, посмотрите на содержимое переменных среды, с которыми вы планируете сравнивать - если у вас установлен PHP, это так же просто, как добавить следующий блок в ваше приложение:

<?php
  var_dump($_SERVER);
?>

... затем напишите свои правила (желательно для тестирования на сервере разработки) и отметьте любые несогласованные совпадения или действия в вашем Apache Журнал ошибок файл.

Для более сложных правил используйте mod_rewrite RewriteLog директива для записи активности в файл и установки RewriteLogLevel 3

Есть ли влияние правил mod_rewrite на SEO или производительность, о которых мне следует знать?

AllowOverride all влияет на производительность сервера, поскольку Apache должен проверять .htaccess файлы и директивы синтаксического анализа с каждым запросом - по возможности сохраните все директивы в конфигурации VirtualHost для вашего сайта или включите .htaccess заменяет только те каталоги, которые в них нуждаются.

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

Роботы поисковых систем предпочитают сопоставление 1: 1 контент: URI (это основа для ранжирования ссылок на контент) - если вы используете mod_rewrite для создания временных перенаправлений или обслуживаете один и тот же контент под несколькими URI, подумайте об указании канонический URI в ваших HTML-документах.

Есть ли распространенные ситуации, когда mod_rewrite может показаться подходящим инструментом для работы, но это не так?

Это огромная (и потенциально спорная) тема сама по себе - лучше (ИМХО) рассматривать варианты использования в каждом конкретном случае и позволить тем, кто задает вопрос, определять, подходят ли предложенные решения их потребностям.

Какие общие примеры?

Уловки и подсказки по mod_rewrite от AskApache охватывает практически все распространенные варианты использования, которые появляются регулярно, однако «правильное» решение для данного пользователя может зависеть от сложности конфигурации пользователя и существующих директив (вот почему обычно рекомендуется посмотреть, какие Другой директивы, которые пользователь использует всякий раз, когда возникает вопрос о mod_rewrite).

Как и многие администраторы / разработчики, я годами боролся с хитросплетениями правил перезаписи и недоволен существующей документацией Apache, поэтому в качестве личного проекта я решил разобраться в том, как mod_rewrite на самом деле работает и взаимодействует с остальной частью ядра Apache, поэтому в течение последних нескольких месяцев я инструментировал тестовые примеры с strace + изучение исходного кода, чтобы разобраться во всем этом.

Вот некоторые ключевые комментарии, которые следует учитывать разработчикам правил перезаписи:

  • Некоторые аспекты перезаписи являются общими для конфигурации сервера, виртуального хоста, каталога, обработки .htaccess тем не мение
  • Некоторая обработка сильно отличается для корневой конфигурации (конфигурация сервера, виртуальный хост и каталог), в отличие от PerDir (.htaccess) обработка.
  • Хуже того, потому что обработка PerDir может почти без разбора запускать цикл ВНУТРЕННЕГО ПОВТОРНОГО ПОВТОРНОГО ПУСКА, корневые элементы конфигурации должны быть написаны с учетом того, что обработка PerDir может вызвать это.

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

  • Те, у кого есть root-доступ к конфигурации Apache. Обычно это администратор / разработчик с выделенным сервером / виртуальной машиной приложения, и сообщение здесь довольно простое: избегайте использования .htaccess файлы, если это возможно; все делать в конфигурации вашего сервера или vhost. Отладка довольно проста, поскольку разработчик может установить отладку и имеет доступ к файлам rewrite.log.

  • Пользователи совместно используемой размещенной службы (SHS).

    • Такие пользователи иметь использовать .htaccess Обработка / Perdir, поскольку альтернативы нет.
    • Хуже того, уровень навыков таких пользователей (в том, что касается использования релейной логики mod_rewrite, управляемой регулярным выражением), как правило, значительно ниже, чем у опытных администраторов.
    • Apache и хостинг-провайдеры не предлагают поддержки отладки / диагностики. Единственная диагностическая информация - это успешное перенаправление, перенаправление на неправильный URI. или код статуса 404/500. Это оставляет их в замешательстве и беспомощности.
    • Apache чрезвычайно слабо объясняет, как работает переписывание для этого варианта использования. Например, он не дает четкого объяснения того, что PerDir .htaccess файл выбран и почему. Он не объясняет тонкости работы PerDir на велосипеде и не объясняет, как этого избежать.

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

Я написал пару статей в блоге (например, Подробнее об использовании правил перезаписи в файлах .htaccess), который охватывает множество подробных моментов, которые я не буду здесь повторять, чтобы этот пост был кратким. У меня есть собственный общий сервис, а также я поддерживаю несколько специализированных проектов и проектов VM FLOSS. Я начал использовать стандартную виртуальную машину LAMP в качестве тестового средства для моей учетной записи SHS, но в конце концов я решил, что лучше создать правильную зеркальную виртуальную машину (описанную Вот).

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

  • Последовательное описание того, как система перезаписи фактически работает при обработке PerDir
  • Набор руководств / лучших практик по написанию .htaccess переписать правила
  • Простой веб-анализатор сценария перезаписи, похожий на синтаксические анализаторы HTML W3C, но с помощью которого пользователи могут вводить тестовые URI или тестовые векторы того же самого и получать немедленный журнал логического потока перезаписи /
  • Подсказки о том, как получить встроенную диагностику из ваших правил (например,

    • Использовать [E=VAR:EXPR] используя тот факт, что EXPR расширит обратные ссылки ($ N или% N), чтобы сделать их доступными в качестве диагностики для целевого скрипта.
    • Если вы заказываете свои правила перезаписи с помощью флагов [OR], [C], [SKIP] и [L], чтобы работала вся схема перезаписи без необходимость использования внутреннего перенаправления, тогда вы можете добавить следующее в качестве правила 1, чтобы избежать всех проблем с циклами:

      RewriteCond %{ENV:REDIRECT_STATUS} !=""
      RewriteRule .  -  [L]
      

Использование rewritemap

Есть много вещей, которые вы можете сделать с перезаписью карт. Карты перезаписи объявляются с помощью директивы Rewritemap и затем могут использоваться как в оценках RewritCond, так и в RewriteRule Subsitutions.

Общий синтаксис RewriteMap:

RewriteMap MapName MapType:MapSource

Например:

RewriteMap examplemap txt:/path/to/file/map.txt

Затем вы можете использовать имя карты для таких конструкций:

${examplemap:key}

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

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritemap

Неэкранирование строк.

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

Например: я хочу проверить строку "cafe" в строке запроса. Однако браузер избежит этого перед отправкой на мой сервер, поэтому мне нужно либо выяснить, что такое экранированная версия URL-адреса для каждой строки, которую я хочу сопоставить, либо я могу просто отменить ее ...

RewriteMap unescape int:unescape

RewriteCond %{QUERY_STRING}  (location|place)=(.*)
RewriteCond ${unescape:%2}   café
RewriteRule ^/find/$         /find/1234? [L,R]

Обратите внимание, как я использую один RewriteCond, чтобы просто захватить аргумент для параметра строки запроса, а затем использовать карту во втором rewriteCond, чтобы отключить его. Затем это сравнивается. Также обратите внимание, что мне нужно использовать% 2 в качестве ключа в rewritemap, поскольку% 1 будет содержать либо «location», либо «place». Когда вы используете круглые скобки для группировки шаблонов, они также будут захвачены, планируете ли вы использовать результат захвата или нет ...

Каковы наиболее распространенные ошибки / подводные камни при написании правил перезаписи?

Очень простая ошибка - это когда вы переписываете URL-адреса, которые изменяют видимый путь, например из /base/1234/index.html к /base/script.php?id=1234. Никакие изображения или CSS с относительными путями к расположению скрипта клиент не найдет. Ряд вариантов решения этой проблемы можно найти на этот FAQ.