Скрытые возможности mod_rewrite

119

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

Какие еще функции / общие проблемы вы использовали mod_rewrite?

Оуэн
источник
5
См. Также serverfault.com/questions/214512/…
Майкл Майерс

Ответы:

203

Где разместить правила 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), который не приведет к повторному синтаксическому анализу вашего запроса.

Оуэн
источник
10
Чувак, сейчас лучшая статья в Интернете о переписывании модов. Я ненавижу эту штуку. Я легкий еретик из- за того, что ненавижу mod_rewrite.
Кент Фредрик
3
Это САМОЕ полезное руководство по mod_rewrite, которое я нашел до сих пор. Простое знание о RewriteLog помогло решить столько проблем, что то, что мне требовалось несколько дней, превратилось в несколько минут. (Я имею в виду, что правила были написаны, но я не мог понять, почему они не работают)
Джо Чин
Пост годичной давности, но одна из самых полезных вещей, которые я нашел на SO - для меня.
Эрик
3
[L]Флаг означает , что правило является последним в текущей обработке, это не остановит перезапись, потому что они являются внутренними переадресовывает, поэтому ваш dirBобратиться к dirCв следующей обработке HTAccess. Само собой RewriteRule ^(.*)$ index.php?query=$1будет бесконечный цикл внутренних перенаправлений (на практике он завершается после 10 итераций). -1, потому что вы предполагаете, что [L] не последний . Это не прекращение процесса перезаписи, но последнее .
kbec
3
Я считаю, RewriteCond %{HTTPS} offчто это предпочтительный способ проверки HTTPS-соединения (в вашем примере принудительного
переноса
22

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

RewriteCond %{ENV:REDIRECT_STATUS} ^$

состояние, как описано здесь .

мромаина
источник
Спасибо, проблема решена!
Мэтью
И мне спасибо, спаситель!
Бенджамин
Это действительно спасатель! Люди должны больше об этом знать. На самом деле, я собираюсь предложить это каждый вопрос о .*с [L]флагом я прочитал , прежде чем я получил здесь.
Qwerty
Я видел несколько модификаций к этому 200, !=200, ^., ^$. Очевидно, переменная устанавливается 200для перенаправления, но также другие страницы (ошибки и прочее) устанавливают для нее какое-то значение. Теперь это означает , что вы либо проверить , если он is empty, is not empty, is 200или is not 200, в зависимости от того, что вам нужно.
Qwerty
18

Сделка с RewriteBase:

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

RewriteBase /
Шон Макмиллан
источник
Ах. Это полностью устранило мою проблему. Спасибо за это!
Tom Savage
3
Каким-либо образом сказать RewriteBase .или указать, что URL-адрес должен оставаться неизменным, просто изменив то, что вы указали?
Jay K,
Спасибо, это была бесценная информация. :)
AturSams
2
Вам нужно только установить, RewriteBaseесли вы используете подстановку относительного пути в RewriteRuleдирективе. Лучше избегать использования относительных путей.
MrWhite
2
Я не согласен с этим ответом. В нашей команде разработчиков мы RewriteBaseполностью избегаем этого , поскольку почти все разработчики неправильно понимают, что он делает. Как сказал @ w3d, он вам нужен только в том случае, если вы хотите сохранить символы и хотите применить одну и ту же базу ко всем своим RewriteRules в одном файле. Ваш код, вероятно, будет понятнее для других, если вы его избегаете.
Саймон Ист
13

Другие подводные камни:

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 

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

Майкл Экока
источник
Спасибо :) Я заметил неожиданные сюрпризы, такие как / log / activity превращаются в /log.txt/activity .. Спасибо за подсказку :) .. слишком плохие компьютеры никогда не развлекают неожиданные вещи, например, случайное соблазнение всех ваших коллег-женщин на facebook :)
AturSams
1
+FollowSymLinksупоминается в документации как обязательный для mod_rewriteработы вообще из соображений безопасности.
Joey
Два утверждения здесь меня очень беспокоят: `` Я не очень разбираюсь во всех возможностях MultiView, но я знаю, что это нарушает мои правила mod_rewrite, когда они активны '' и этот: `` Это, я действительно не знаю подробностей , но я видел это много раз, так что просто сделай это ». Я хочу, чтобы такие люди, как вы, не писали ответы на SO о вещах, в которых вы не уверены.
TheCarver
1
@PaparazzoKid: Я думаю, вы ошибаетесь ТАК за энциклопедию. Это сообщество людей, которые собираются вместе, чтобы составить представление о технологиях, с которыми они работают. В отличие от А. В. Уайта и Джоуи до вас, ваш комментарий практически лишен ценности. MV и FSL - это два из многих вариантов Apache. Отвечаю о подводных камнях при работе конкретно с mod_rw, отдельным модулем, который конфликтует с одними опциями и работает с другими. Я объяснил, как MV влияет на mod_rw, и упомянул, что + FSL - популярная рекомендация. Джои подтвердил, что это действительно обязательно. Что вы приносите на стол?
Майкл Экока
Спасибо. Я просто потратил большую часть часа на то, чтобы заставить старый сайт работать и пытаться отладить правила перезаписи, только чтобы обнаружить, что MultiViews переопределяет все это.
Эндрю МакКомб
5

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

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]
DrDol
источник
4

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

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

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

Так в [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), который не приведет к повторному синтаксическому анализу вашего запроса.

JaredC
источник
3

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

Они похожи на замену "ключ-значение":

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

Затем вы можете использовать сопоставление в своих правилах, например:

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

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

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

БЫТЬ
источник
Игнорируйте эту функцию, если вы используете .htaccessперезапись на основе. В этом контексте это не работает.
TerryE
2
Директива RewriteMap должна использоваться в контексте сервера (httpd.conf), но после ее определения вы можете использовать карту через RewriteRule в файле .htaccess.
JaredC
2

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

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

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

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

RewriteRule .* - [R=503,L]

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

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

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

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

Найдите время, чтобы изучить его, оно того стоит! :)

еженедельно
источник