Скрытые возможности mod_rewrite - PullRequest
119 голосов
/ 13 ноября 2008

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

С какими другими функциями / общими проблемами вы сталкивались при использовании mod_rewrite?

Ответы [ 8 ]

203 голосов
/ 13 ноября 2008

Где разместить правила для mod_rewrite

Правила

mod_rewrite могут быть размещены в файле httpd.conf или в файле .htaccess. если у вас есть доступ к httpd.conf, размещение правил здесь будет способствовать повышению производительности (поскольку правила обрабатываются один раз, а не каждый раз, когда вызывается файл .htaccess).

Регистрация запросов mod_rewrite

Ведение журнала может быть включено из файла httpd.conf (включая <Virtual Host>):

# logs can't be enabled from .htaccess
# loglevel > 2 is really spammy!
RewriteLog /path/to/rewrite.log
RewriteLogLevel 2

Распространенные случаи использования

  1. Чтобы направить все запросы в одну точку:

    RewriteEngine on
    # ignore existing files
    RewriteCond %{REQUEST_FILENAME} !-f   
    # ignore existing directories
    RewriteCond %{REQUEST_FILENAME} !-d   
    # map requests to index.php and append as a query string
    RewriteRule ^(.*)$ index.php?query=$1 
    

    Начиная с Apache 2.2.16 вы также можете использовать FallbackResource.

  2. Обработка перенаправлений 301/302:

    RewriteEngine on
    # 302 Temporary Redirect (302 is the default, but can be specified for clarity)
    RewriteRule ^oldpage\.html$ /newpage.html [R=302]  
    # 301 Permanent Redirect
    RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 
    

    Примечание : внешние перенаправления неявно 302 перенаправления:

    # this rule:
    RewriteRule ^somepage\.html$ http://google.com
    # is equivalent to:
    RewriteRule ^somepage\.html$ http://google.com [R]
    # and:
    RewriteRule ^somepage\.html$ http://google.com [R=302]
    
  3. Принудительный SSL

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. Общие флаги:

    • [R] или [redirect] - принудительное перенаправление (по умолчанию временное перенаправление 302)
    • [R=301] или [redirect=301] - принудительное перенаправление 301
    • [L] или [last] - прекратить процесс перезаписи (см. Примечание ниже по распространенным ошибкам) ​​
    • [NC] или [nocase] - укажите, что сопоставление должно быть без учета регистра


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

    Вы можете разделить несколько флагов запятой:

    RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
    

Общие подводные камни

  1. Смешивание mod_alias стиль перенаправлений с mod_rewrite

    # Bad
    Redirect 302 /somepage.html http://example.com/otherpage.html
    RewriteEngine on
    RewriteRule ^(.*)$ index.php?query=$1
    
    # Good (use mod_rewrite for both)
    RewriteEngine on
    # 302 redirect and stop processing
    RewriteRule ^somepage.html$ /otherpage.html [R=302,L] 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # handle other redirects
    RewriteRule ^(.*)$ index.php?query=$1                 
    

    Примечание : вы можете смешивать mod_alias с mod_rewrite, но это требует больше работы, чем просто обработка основных перенаправлений, как указано выше.

  2. Контекст влияет на синтаксис

    В файлах .htaccess начальная косая черта не используется в шаблоне RewriteRule:

    # given: GET /directory/file.html
    
    # .htaccess
    # result: /newdirectory/file.html
    RewriteRule ^directory(.*)$ /newdirectory$1
    
    # .htaccess
    # result: no match!
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # httpd.conf
    # result: /newdirectory/file.html
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # Putting a "?" after the slash will allow it to work in both contexts:
    RewriteRule ^/?directory(.*)$ /newdirectory$1
    
  3. [L] не последний! (Иногда)

    Флаг [L] останавливает обработку любых дальнейших правил перезаписи для этого прохода через набор правил . Однако, если URL был изменен на этом этапе, и вы находитесь в контексте .htaccess или в разделе <Directory>, то ваш измененный запрос будет снова возвращен через механизм синтаксического анализа URL. И на следующем проходе это может соответствовать другому правилу на этот раз. Если вы этого не понимаете, часто выглядит, что ваш флаг [L] не имел никакого эффекта.

    # processing does not stop here
    RewriteRule ^dirA$ /dirB [L] 
    # /dirC will be the final result
    RewriteRule ^dirB$ /dirC     
    

    Наш журнал перезаписи показывает, что правила запускаются дважды, а URL обновляется дважды:

    rewrite 'dirA' -> '/dirB'
    internal redirect with /dirB [INTERNAL REDIRECT]
    rewrite 'dirB' -> '/dirC'
    

    Лучший способ обойти это - использовать флаг [END] ( см. Документы Apache ) вместо флага [L], если вы действительно хотите остановить всю дальнейшую обработку правил (и последующие проходы ). Однако флаг [END] доступен только для Apache v2.3.9 + , поэтому, если у вас версия v2.2 или ниже, вы застряли только с флагом [L].

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

    # Only process the following RewriteRule if on the first pass
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ...
    

    Или вы должны убедиться, что ваши RewriteRule находятся в контексте (то есть httpd.conf), который не вызовет повторный анализ вашего запроса.

21 голосов
/ 22 апреля 2010

, если вам нужно «заблокировать» внутренние перенаправления / перезаписи в .htaccess, посмотрите на

RewriteCond %{ENV:REDIRECT_STATUS} ^$

условие, как обсуждается здесь .

18 голосов
/ 27 августа 2009

Сделка с RewriteBase:

Вам почти всегда нужно устанавливать RewriteBase. Если вы этого не сделаете, apache догадывается, что ваша база - это путь физического диска к вашему каталогу. Итак, начните с этого:

RewriteBase /
13 голосов
/ 19 августа 2009

Другие ловушки:

1- Иногда хорошая идея отключить MultiViews

Options -MultiViews

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

Я объясню: Предположим, у вас есть 2 php-файла в вашем веб-каталоге, file1.php и file2.php, и вы добавляете эти условия и правила в ваш .htaccess:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ file1.php/$1 

Предполагается, что все URL, которые не соответствуют файлу или каталогу, будут захвачены файлом file1.php. Сюрприз! Это правило не соблюдается для URL http://myhost/file2/somepath. Вместо этого вы попали внутрь file2.php.

То, что происходит, заключается в том, что MultiViews автоматически догадались, что URL, который вы на самом деле хотели, был http://myhost/file2.php/somepath, и с радостью перенесли вас туда.

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

Хорошо, короче, если вы хотите, чтобы mod_rewrite работал так, чтобы приближаться к логике, отключение MultiViews - это шаг в правильном направлении.

2 - включить FollowSymlinks

Options +FollowSymLinks 

Этот, я действительно не знаю деталей, но я видел, что это упоминалось много раз, так что просто сделайте это.

5 голосов
/ 20 января 2010

Уравнение можно выполнить с помощью следующего примера:

RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC]
# %1 is the string that was found above
# %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;"
#RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*)
# <> is used as an string separator/indicator, can be replaced by any other character
RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC]
RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L]

Динамическая балансировка нагрузки:

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

RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC]
RewriteRule (.*) https://worker%1.internal.com/$1 [P,L]
4 голосов
/ 14 ноября 2014

Лучшее понимание флага [L] в порядке. [L] флаг является последним, вам просто нужно понять, что приведет к тому, что ваш запрос будет снова перенаправлен через механизм анализа URL. Из документов (http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l) (выделено мной):

Флаг [L] приводит к тому, что mod_rewrite прекращает обработку набора правил. В в большинстве контекстов это означает, что если правило соответствует, никаких дальнейших правил будет обработан. Это соответствует последней команде в Perl, или команда перерыва в C. Используйте этот флаг, чтобы указать, что текущий Правило должно применяться немедленно, без учета дальнейших правил.

Если вы используете RewriteRule в файлах .htaccess или в разделах <Directory> , важно иметь некоторое представление о как правила обрабатываются. Упрощенная форма этого заключается в том, что когда-то правила обработаны, переписанный запрос передается механизм парсинга URL, чтобы делать с ним что угодно. Возможно, что при обработке переписанного запроса файл .htaccess или <Directory> раздел может встретиться снова, и, таким образом, набор правил может быть запущен снова с самого начала. Чаще всего это произойдет, если один из правила вызывает перенаправление - внутреннее или внешнее - вызывая запросить процесс, чтобы начать сначала.

Таким образом, флаг [L] действительно останавливает обработку любых дальнейших правил перезаписи для , которые пропускают через набор правил. Однако, если ваше правило, помеченное [L], изменило запрос, и вы находитесь в контексте .htaccess или в разделе <Directory>, тогда ваш измененный запрос будет снова возвращен через механизм синтаксического анализа URL. И на следующем проходе это может соответствовать другому правилу на этот раз. Если вы не понимаете, что произошло, похоже, ваше первое правило перезаписи с флагом [L] не дало результата.

Лучший способ обойти это - использовать флаг [END] (http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end) вместо флага [L], если вы действительно хотите остановить всю дальнейшую обработку правил (и последующий повторный анализ). Однако, флаг [END] доступен только для Apache v2.3.9 +, поэтому, если у вас версия v2.2 или ниже, вы застряли только с флагом [L]. В этом случае вы должны полагаться на операторы RewriteCond, чтобы предотвратить сопоставление правил для последующих проходов механизма синтаксического анализа URL. Или вы должны убедиться, что ваши RewriteRule находятся в контексте (то есть httpd.conf), который не приведет к повторному анализу вашего запроса.

3 голосов
/ 19 августа 2009

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

Они как замена ключевого значения:

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

Тогда вы можете использовать отображение в ваших правилах, например:

RewriteRule ^/ex/(.*) ${examplemap:$1}

Более подробную информацию по этой теме можно найти здесь:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

2 голосов
/ 12 февраля 2014

mod_rewrite может изменять аспекты обработки запросов без изменения URL, например, установка переменных среды, установка файлов cookie и т. д. Это невероятно полезно.

Условно установите переменную окружения:

RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC]
RewriteRule .* - [E=MY_ENV_VAR:%b]

Вернуть ответ 503: Флаг RewriteRule [R] может принимать значение, отличное от 3xx, и возвращать ответ без перенаправления, например, для управляемого времени простоя / обслуживания:

RewriteRule .* - [R=503,L]

вернет ответ 503 (не перенаправление per se).

Кроме того, mod_rewrite может действовать как мощный интерфейс для mod_proxy, так что вы можете сделать это вместо написания директив ProxyPass:

RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]

Мнение: Использование RewriteRule s и RewriteCond s для маршрутизации запросов к различным приложениям или балансировщикам нагрузки на основе практически любого возможного аспекта запроса является просто невероятно мощным. Управление запросами на пути к бэкэнду и возможность изменять ответы на обратном пути делает mod_rewrite идеальным местом для централизации всей конфигурации, связанной с маршрутизацией.

Потратьте время, чтобы выучить это, оно того стоит! :)

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