Перенаправление, изменение URL-адресов или перенаправление HTTP на HTTPS в Apache - все, что вы хотели знать о правилах Mod_Rewrite, но боялись спросить
Это канонический вопрос о mod_rewrite в Apache.
Изменение URL-адреса запроса или перенаправление пользователей на URL, отличный от того, который они первоначально запрашивали, выполняется с помощью mod_rewrite. Это включает в себя такие вещи, как:
- Изменение HTTP на HTTPS (или наоборот)
- Изменение запроса на страницу, которая больше не существует, на новую замену.
- Изменение формата URL (например,? Id=3433 в /id/3433)
- Представление другой страницы на основе браузера, на основе реферера, на основе всего, что возможно под луной и солнцем.
- Все, что вы хотите возиться с URL
Все, что вы когда-либо хотели знать о правилах Mod_Rewrite, но боялись спросить!
Как я могу стать экспертом в написании правил mod_rewrite?
- Каков основной формат и структура правил mod_rewrite?
- Какую форму / вид регулярных выражений мне нужно иметь твердое представление?
- Каковы наиболее распространенные ошибки / подводные камни при написании правил переписывания?
- Что такое хороший метод для тестирования и проверки правил mod_rewrite?
- Есть ли какие-то последствия для правил mod_rewrite для SEO или производительности, о которых мне следует знать?
- Есть ли общие ситуации, когда mod_rewrite может показаться подходящим инструментом для работы, но это не так?
- Каковы некоторые общие примеры?
Место для проверки ваших правил
Веб-сайт htaccess tester - отличное место, чтобы поиграть с вашими правилами и проверить их. Он даже показывает выходные данные отладки, чтобы вы могли видеть, что соответствует, а что нет.
5 ответов
порядок синтаксиса 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-реферер принадлежит faultserver.ru, перенаправляйте запросы блога на специальные страницы 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 попадают на специальные страницы с ошибками сервера, а не только с ссылками, указывающими, что они пришли отсюда. Это явно не цель написания этих правил. Это можно сделать с помощью нескольких правил 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 неявно объединяются и могут быть явно ORed. Для обработки ссылок от ServerFault и Super User (явное ИЛИ):
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
Этот регистр будет соответствовать любой строчной букве во втором совпадающем выражении и делать это для максимально возможного количества символов. \.
Конструкция говорит, что он должен рассматривать период как фактический период, а не как специальный символ, который используется в предыдущих примерах. Это сломается, если имя файла содержит тире.
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 flags
Флаги в правилах перезаписи имеют множество специальных значений и вариантов использования.
RewriteRule ^/blog/([0-9]{4})/([-a-z]*).\html /newblog/$1/$2.shtml [L]
Флаг это [L]
в конце вышеприведенного выражения. Можно использовать несколько флагов, разделенных запятой. Связанная документация описывает каждого, но вот они в любом случае:
L = последний. Прекратите обработку RewriteRules, как только этот совпадет. Заказ имеет значение!
C = цепь Продолжить обработку следующего RewriteRule. Если это правило не соответствует, то следующее правило не будет выполнено. Подробнее об этом позже.
E = установить переменную среды. Apache имеет различные переменные среды, которые могут влиять на поведение веб-сервера.
F = запрещено. Возвращает ошибку 403-Forbidden, если это правило соответствует.
G = Ушел. Возвращает ошибку 410-Gone, если это правило соответствует.
H = Обработчик. Принудительно обрабатывает запрос, как если бы он был указанным MIME-типом.
N = Далее. Заставляет правило начать все заново и заново сопоставить. БЫТЬ ОСТОРОЖЕН! Петли могут привести.
NC = Нет дела. Позволяет jpg
соответствовать как JPG, так и JPG.
NE = нет выхода. Предотвращает перезапись специальных символов (.? # & Etc) в их эквиваленты в шестнадцатеричном коде.
NS = Нет подзапросов. Если вы используете серверные включения, это предотвратит совпадение с включенными файлами.
P = прокси. Принудительно обрабатывает правило с помощью mod_proxy. Прозрачно предоставлять контент с других серверов, потому что ваш веб-сервер получает его и повторно обслуживает. Это опасный флаг, так как плохо написанный превратит ваш веб-сервер в открытый прокси, и это плохо.
PT = Pass Through. Примите во внимание операторы Alias в соответствии RewriteRule.
QSA = QSAppend. Если исходная строка содержит запрос ( http://example.com/thing?asp=foo), добавьте исходную строку запроса к переписанной строке. Обычно это будет отброшено. Важно для динамического контента.
R = Перенаправление. Предоставьте HTTP-перенаправление на указанный URL-адрес. Может также предоставить точный код перенаправления [R=303]. Очень похоже на RedirectMatch
, который быстрее и должен использоваться, когда это возможно.
S = Пропустить. Пропустите это правило.
T = Тип. Укажите 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 имеет флаг Chain, второе правило перезаписи будет выполнено, когда первое выполнит, то есть, когда будет найдено предыдущее правило 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.
Я бы порекомендовал AskApache mod_rewrite Учебник по отладке для полного списка переменных, которые могут быть доступны для mod_rewrite.
Каковы наиболее распространенные ошибки / подводные камни при написании правил переписывания?
Большинство проблем с RewriteRule проистекают из-за неправильного понимания синтаксиса PCRE / неспособности должным образом экранировать специальные символы или отсутствия понимания содержимого переменных, используемых для сопоставления.
Типичные проблемы и рекомендуемые способы устранения неполадок:
- 500 - Внутренняя ошибка сервера - Удалите элементы управления кареткой Windows в конфигурационном файле (ах), если они есть, убедитесь, что mod_rewrite включен (директивы переноса в
IfModule
условно избегать этого сценария), проверьте синтаксис директив, закомментируйте директивы, пока проблема не будет выявлена - Цикл перенаправления - используйте RewriteLog и RewriteLogLevel, закомментируйте директивы, пока проблема не будет выявлена
Что такое хороший метод для тестирования и проверки правил mod_rewrite?
Во-первых, посмотрите на содержимое переменных среды, с которыми вы планируете сравнивать - если у вас установлен PHP, это так же просто, как добавить следующий блок в ваше приложение:
<?php
var_dump($_SERVER);
?>
... затем напишите свои правила (желательно для тестирования на сервере разработки) и запишите все несоответствия или действия в файле Apache ErrorLog.
Для более сложных правил используйте 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 может показаться подходящим инструментом для работы, но это не так?
Это огромная (и потенциально спорная) тема сама по себе - лучше (ИМХО) решать вопросы использования в каждом конкретном случае и позволять спрашивающим определять, соответствуют ли предложенные резолюции их потребностям.
Каковы некоторые общие примеры?
Полезные советы и советы ModArewache от AskApache охватывают практически все распространенные варианты использования, которые регулярно появляются, однако "правильное" решение для данного пользователя может зависеть от сложности конфигурации пользователя и существующих директив (именно поэтому обычно это хорошая идея, чтобы увидеть, какие другие директивы имеет пользователь при возникновении вопроса mod_rewrite).
Как и многие администраторы и разработчики, я годами боролся со сложностями правил переписывания и недоволен существующей документацией Apache, поэтому я решил, как личный проект, разобраться в том, как mod_rewrite
на самом деле работает и взаимодействует с остальной частью ядра Apache, поэтому в течение последних нескольких месяцев я тестировал контрольные примеры с strace
+ углубление в исходный код, чтобы справиться со всем этим.
Вот некоторые ключевые комментарии, которые необходимо учитывать разработчикам правил переписывания:
- Однако некоторые аспекты перезаписи являются общими для конфигурации сервера, виртуального хоста, каталога, обработки.htaccess.
- Некоторая обработка сильно отличается для корневого конфига (конфигурации сервера, виртуального хоста и каталога) в отличие от PerDir (
.htaccess
обработка. - Хуже того, поскольку обработка PerDir может почти беспорядочно запускать циклы ВНУТРЕННЕГО НАПРАВЛЕНИЯ, корневые элементы конфигурации должны быть записаны с учетом того, что такая обработка PerDir может инициировать это.
Я хотел бы сказать, что из-за этого вам почти нужно разделить сообщества переписанных пользователей на две категории и рассматривать их как совершенно отдельные:
Те, у кого есть root-доступ к конфигурации Apache. Как правило, это администратор / разработчик с выделенным сервером приложений / виртуальной машиной, и сообщение здесь довольно простое: избегайте использования
.htaccess
файлы, если это вообще возможно; сделать все на вашем сервере или vhost config. Отладка достаточно проста, так как разработчик может установить отладку и имеет доступ к файлам 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
Есть много вещей, которые вы можете сделать с переписанными картами. Rewritemaps объявляются с использованием директивы Rewritemap и могут затем использоваться как в оценках RewritCond, так и в подразделах RewriteRule.
Общий синтаксис для RewriteMap:
RewriteMap MapName MapType:MapSource
Например:
RewriteMap examplemap txt:/path/to/file/map.txt
Затем вы можете использовать mapname для таких конструкций:
${examplemap:key}
Карта содержит пары ключ / значение. Если ключ найден, значение заменяется. Простые карты - это просто текстовые файлы, но вы можете использовать хэш-карты и даже запросы SQL. Более подробная информация в документах:
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
Unescaping строки.
Есть четыре внутренних карты, которые вы можете использовать для некоторых манипуляций. Особенно беспрепятственные струны могут пригодиться.
Например: я хочу проверить строку "café" в строке запроса. Тем не менее, браузер избежит этого перед отправкой на мой сервер, поэтому мне нужно будет либо выяснить, что такое экранированная версия URL для каждой строки, которую я хочу сопоставить, либо я могу просто удалить ее...
RewriteMap unescape int:unescape
RewriteCond %{QUERY_STRING} (location|place)=(.*)
RewriteCond ${unescape:%2} café
RewriteRule ^/find/$ /find/1234? [L,R]
Обратите внимание, как я использую один RewriteCond для захвата аргумента в параметре строки запроса, а затем использую карту во втором rewriteCond для его удаления. Это тогда сравнивается. Также обратите внимание, что мне нужно использовать%2 в качестве ключа в карте перезаписи, так как%1 будет содержать "location" или "place". Когда вы используете скобки для группировки шаблонов, они также будут захвачены, независимо от того, планируете ли вы использовать результат захвата или нет...
Каковы наиболее распространенные ошибки / подводные камни при написании правил переписывания?
Очень простой подводный камень - когда вы переписываете URL, которые изменяют видимый путь, например, из/base/1234/index.html
в /base/script.php?id=1234
, Любые изображения или CSS с относительными путями к местоположению скрипта не будут найдены клиентом. Несколько вариантов решения этой проблемы можно найти в этом FAQ.