Loading...
X

Обработка строки параметров URI в mod_rewrite


Запросы: request и query

Чтобы не возникло путаницы, начнём с терминов. В английском языке используются два слова request и query. Проблема в том, что они переводятся одинаково как «запрос». При этом они могут использоваться в одном контексте — в английском языке это не вызывает путаницы, а в русском получается «запрос» и «запрос», которые означают разные вещи.

Слово request относится к HTTP запросу. Протокол HTTP передаёт запрос на сервер, в этом запросе содержаться заголовки (например, информация об интересующем хосте (сайте), User-Agent пользователя, имеющиеся для этого сайта кукиз и прочее). Также говорят об URI запроса, методе запроса и так далее. Среди передаваемой информации также имеется query — строка запроса.

К примеру, если я в веб-браузере открою страницу suay.ru/wp-admin/post.php?post=1673&action=edit, то в ней строкой запроса (query) будет post=1673&action=edit, то есть всё то, что следует после ? (знака вопроса).

Как в mod_rewrite искать по параметрам после знака вопроса

Нужно понимать эту разницу, так как по умолчанию mod_rewrite работает с REQUEST_URI, то есть частью запроса, которая НЕ включает в себя строку query. Причём RewriteRule просто игнорирует query.

Очень часто mod_rewrite используется для ЧПУ («красивых» адресов), когда нужно сделать преобразования вида: из

  • site.com/phones/infos/4/52

в

  • site.com/index.php?category=phones&subcat=infos&idc=4&marea=52

В этом случае, как мы видим, исходная строка не содержит строки запроса (query), поэтому при написании правила проблема игнорирования query не возникает:

RewriteEngine on
RewriteRule ([A-Za-z]+)/([A-Za-z]+)/([0-9]+)/([0-9]+) index.php?category=$1&subcat=$2&idc=$3&marea=$4

А что если нам нужно сделать преобразование другого рода:

Из:

http://site.com/category/infos/pages.php?idc=4&marea=52

В:

http://site.com/category/infos/pages.php?marea=52

То есть мы убираем из строки запроса (query) параметр idc вместе с его значением.

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

Проблема решается с использованием директивы RewriteCond, которая умеет работать с переменной QUERY_STRING, содержащей строку query с запросом.

Что нам нужно знать про RewriteCond? Директива RewriteCond оказывает воздействие на RewriteRule, которое следует после строки (или блока строк) RewriteCond. В RewriteCond также можно использовать обратные ссылки, но для разграничения от обратных ссылок RewriteRule, вместо $ (знака доллара) используется % (знак процента), то есть вместо $1, первая обратная ссылка обозначается как %1.

Итак, составляем RewriteCond для нашего случая:

RewriteCond "%{QUERY_STRING}" "idc=([0-9]+)&marea=([0-9]+)$"

Рассмотрим эту строку подробно.

%{QUERY_STRING} означает, что анализируется строка запроса, следующая в URI после знака вопроса.

Строка idc=([0-9]+)&marea=([0-9]+)$ представляет собой шаблон поиска. То есть ищется строка, которая начинается на idc=, затем следуют цифры (одна или более) — причём, поскольку это регулярное выражение в скобках, то найденные цифры помещаются в первую обратную ссылку, затем идёт строка &marea=, затем опять цифры (одна или более), причём найденные цифры помещаются во вторую обратную ссылку. В данном случае символ $ означает конец анализируемой строки.

Чего мы этим добились? Мы сделали так, что значение idc будет помещено в первую обратную ссылку, а значение marea будет помещено во вторую обратную ссылку.

Переходим непосредственно к правилу перезаписи RewriteRule:


RewriteRule "(.*)" "/$1?marea=%2" [L,R]

Рассмотрим это содержимое подробно:

Регулярное выражение (.*) означает «что угодно». Но мы помним, что это «что угодно» игнорирует строку запроса query. То есть это URI без строки query. Причём, поскольку это регулярное выражение в скобках, то оно помещается в первую обратную ссылку.

Далее строка /$1?marea=%2. Она начинается с указания на корневую папку сайта (/), затем идёт первая обратной ссылка $1, в неё помещается весь URI кроме строки запроса, затем добавляется строка ?marea=, к которой добавляется то, что содержится во второй обратной ссылке (%2) от RewriteCond.

Флаги [L,R] означают завершить проверку по другим правилам и сделать редирект на новый адрес (то есть адрес страницы измениться в адресной строке веб-браузера пользователя).

В результате получится, что адрес:

  • http://site.com/category/infos/pages.php?idc=4&marea=52

Превратится в:

  • http://site.com/category/infos/pages.php?marea=52

Соберём условие перезаписи и правило перезаписи вместе:

RewriteEngine on
RewriteCond "%{QUERY_STRING}" "idc=([0-9]+)&marea=([0-9]+)$"
RewriteRule "(.*)" "/$1?marea=%2" [L,R]

Как с mod_rewrite удалить параметр из URI

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

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    ^page=1$
RewriteRule      (.*)               $1?     [R=permanent]

Построчно:

  1. Вы включаете функцию перезаписи.
  2. В качестве условия («оператор если») вы указываете, что строка запроса должна быть точно page=1, чтобы применялись следующие правила.
  3. Затем вы указываете правило, которое говорит, что нужно заменить весь путь (.*) на него же ($1), но сделать строку запроса пустой (?) и на этот результат выполняется постоянный редирект permanent redirect (301).

Если вы хотите, чтобы перенаправление было временным (302), вы можете просто удалить часть =permanent. Moved Temporarily (временно перемещено) это значение по умолчанию для флага R.

Перемещено Временно - значение по умолчанию для флага R.

Как удалить часть строки запроса после знака вопроса

Выше показан пример когда удаляется вся строка запроса. Но что если нужно удалить только один параметр из строки запроса URI, а другой оставить?


Чтобы было понятнее, рассмотрим следующую ситуацию: необходимо из адреса страницы удалить строку «&PageSpeed=noscript», при этом необходимо сохранить параметр «p=».

Рассмотрим конкретный пример. Необходимо, чтобы страница с адресом:

https://site.net/?p=6413&PageSpeed=noscript

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


https://site.net/?p=6413

Эту задачу решает следующее правило:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "p=(.+)&PageSpeed=noscript"
RewriteRule      (.*)               /$1?p=%1     [R=permanent]

Вторая строка ищет в страке запроса совпадение с шаблоном «p=(.+)&PageSpeed=noscript». Это означает, что за переменной «p=» может следовать что угодно (в шаблоне обозначается как «.+»), а затем следует строка «&PageSpeed=noscript». Поскольку часть шаблона со значением «что угодно» помещена в круглые скобки (.+), то оно становится значением обратной ссылки.

Затем (в третьей строке) указано правило, которое говорит, что нужно заменить весь путь (.*) на него же $1, а затем добавить строку «?p=» после которой будет помещено значение обратной ссылки %1 из %{QUERY_STRING}. То есть заново формируется строка запроса с прежним значением «p=», но при этом строка «&PageSpeed=noscript» оказывается отброшенной.

Таким же образом, используя обратные ссылки с номерами два (%2), три (%3) и так далее можно сохранить значение нескольких переменных из строки запроса, а остальные удалить с помощью mod_rewrite.

Поскольку для указанного сайта значением «p=» может быть только число, то вместо указания «что угодно» (.+) можно указать диапазон допустимых символов (в данном случае только цифры) как ([0-9]+). В результате правило будет выглядеть так:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "p=([0-9]+)&PageSpeed=noscript"
RewriteRule      (.*)               /$1?p=%1     [R=permanent]

mod_rewrite: отрицание в строке запроса %{QUERY_STRING}

Рассмотрим отрицание в строках запроса, то есть когда правило RewriteRule применяется только к запросам, в которых у query отсутствует определённая строка. Для этого перед Шаблоном укажите ! (восклицательный знак) или конструкцию !=. Также Шаблон не нужно помещать в кавычки, иначе условие перестаёт работать. В результате RewriteRule будет применено только к запросам без указанной строки в query (в данном случае без fromsubdomain=true).

RewriteEngine On
RewriteCond "%{QUERY_STRING}" !=fromsubdomain=true [NC]
RewriteRule subdomain/([^/]+) https://$1.suay.ru

Ошибка ERR_TOO_MANY_REDIRECTS при удалении строки запроса. Как использовать флаг QSD для удаления строки запроса

Рассмотрим следующий пример, который удаляет из запроса строку «next=0», но сохраняет значение переменной «cat=»:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^cat=(.+)&next=0$"
RewriteRule      (.*)               /$1?cat=%1     [R=permanent]

При тестах это правило работает как ожидается.

Немного изменим это правило, чтобы удалять строку «next=0» даже из запросов, в которых отсутствует «cat=»:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule     (.*)               /$1     [R=permanent]

В этом примере если в запросе имеется строка «next=0» и при этом этом другие переменные отсутствуют, то делается перенаправление на главную страницу.

То есть при попытке открыть адрес

https://site.net/?next=0

должна быть сделана переадресация на главную страницу сайта:

https://site.net/

Вместо ожидаемого результата, этот как казалось бы более простой пример выдаёт неожиданную ошибку:

This page isn’t working
web.site redirected you too many times.
Try deleting your cookies.
ERR_TOO_MANY_REDIRECTS

Суть ошибки в бесконечных редиректах (перенаправлениях, переадресации). То есть вместо того, перейти на главную страницу, правило mod_rewrite отправляет пользователю на один и тот же адрес снова и снова.

Чтобы понять причину ошибки, нужно знать правила, по которым со строкой запроса работает веб-сервер Apache. Эти правила следующие:

  1. По умолчанию, строка запроса сохраняется и пристыковывается к конечному полученному результату.
  2. Если пользователь как-либо указал собственную строку запроса, то начальная строка запроса полностью удаляется и используется то, что создал пользователь.
  3. Даже если пользователь не указал собственную строку запроса, с помощью флага QSD можно поменять поведение по умолчанию и отбрасывать строку запроса.

Подробности о флаге QSD|qsdiscard (отбросить любую строку запроса из входящего URI) смотрите по следующему адресу: https://hackware.ru/?p=5564#310

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

В рабочем примере приведённом немного выше, мы не столкнулись с этой проблемой поскольку для сохранения переменной «cat=» мы указывали новую строку запроса, а старая отбрасывалась.

Итак, если мы не создаём новую строку запроса и хотим полностью отбросить старую, есть как минимум два способа это сделать.

Во-первых, можно использовать флаг QSD (Query String Discard) для перенаправления без передачи строки запроса:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule     (.*)               /$1     [R=permanent]

Чуть видоизменённое правило (для улучшения его читаемости):

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule      .*               /     [QSD,R=permanent]

Помните, что флаг QSD доступен с Apache версии 2.4.0 и более поздних. Версии Apache до 2.4, которые не поддерживают QSD, при использовании этого флага вернут ошибку 500.

Вторым, более универсальным вариантом, в том числе подходящим для более ранних версий Apache, является использование знака вопроса (?) в конце целевого URL-адреса. Это эквивалентно указанию пустой строки запроса.

Пример правила mod_rewrite, которое полностью удаляет строку запроса, если она соответствует указанному шаблону:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule      .*               /?     [R=permanent]

Этот вариант работает на любых версиях Apache.


Ничего не поняли? Значит начните изучение mod_rewrite с самых основ. Подробный учебник по mod_rewrite на русском языке здесь.


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

Авиабилеты на международные и местные направления по минимальным ценам:

Рекомендуется вам:


Buy Me a Coffee

Leave Your Observation

Ваш адрес email не будет опубликован. Обязательные поля помечены *

wp-puzzle.com logo