Приховані особливості mod_rewrite


119

Здається, mod_rewriteостаннім часом пливе навколо пристойне число ниток, дещо плутається, як працюють певні його аспекти. Як результат, я склав кілька приміток про загальну функціональність і, можливо, кілька прикрих нюансів.

Які ще функції / поширені проблеми у вас виникли mod_rewrite?


Відповіді:


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
Чувак, абсолютно найкраща стаття в Інтернеті зараз про переписування моди. Я ненавиджу цю річ. Я є lighttpd єретиком через те, наскільки я ненавиджу mod_rewrite.
Кент Фредрік

3
Це було найкориснішим посібником, який я знайшов дотепер на mod_rewrite. Щойно дізнатися про RewriteLog допомогло виправити стільки проблем, що те, що потребувало мене днів, щоб відстежити, перетворилося на кілька хвилин. (Я маю на увазі правила були написані, але я не міг зрозуміти, чому вони не працюють)
Джо Чін

1-річна посада, але одна з найбільш корисних речей, яку я знайшла на SO - для мене.
Ерік

3
[L]Прапор означає , що правило є останнім в поточній обробці, це не зупинить перезапис, тому що вони є внутрішніми переадресовує, тому ваш dirBзвернутися до dirCв наступній обробці HTAccess. Поодинці RewriteRule ^(.*)$ index.php?query=$1буде нескінченна петля внутрішніх переадресацій (на практиці вона припиняється через 10 ітерацій). -1 тому що ви припускаєте, що [L] не останній . Це не припинення процесу переписування, але це останнє .
kbec

3
Я вважаю, що RewriteCond %{HTTPS} offце кращий спосіб перевірити наявність HTTPS-зв’язку (у вашому прикладі примушування не-ssl трафіку до HTTPS)
Madbreaks

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. Якщо ви цього не зробите, апаш здогадується, що вашою базою є шлях фізичного диска до вашого каталогу. Тож почніть з цього:

RewriteBase /

Ага. Це повністю вирішило проблему, яку я мав. Дякую за це!
Том Савідж

3
Будь-який спосіб сказати RewriteBase .чи щось вказувати на те, що він повинен зберігати URL-адресу однаковою, просто змінюючи вказане вами?
Jay K

Дякую, це була безцінна інформація. :)
AturSams

2
Вам потрібно встановити лише RewriteBaseякщо ви використовуєте відносну заміну шляху в RewriteRuleдирективі. Краще уникати використання відносних шляхів.
MrWhite

2
Я не згоден з цією відповіддю. У нашій команді розробників ми уникаємо RewriteBaseвзагалі, оскільки майже всі розробники неправильно розуміють, що це робить. Як сказав @ w3d, він вам потрібен лише в тому випадку, якщо ви хочете зберегти символи і хочете застосувати ту саму базу до всіх своїх RewriteRules в одному файлі. Ваш код, ймовірно, стане зрозумілішим для інших, якщо ви цього уникаєте.
Саймон Схід

13

Інші підводні камені:

1- Іноді корисно відключити MultiViews

Options -MultiViews

Я не добре знаю всі можливості MultiViews, але я знаю, що він переплутує мої правила mod_rewrite, коли це активно, тому що одна з його властивостей - це спробувати і "здогадатися" про розширення файлу, який він вважає, що я шукаю .

Я поясню: припустимо, у вашому веб-режимі, file1.php та file2.php у вас є 2 файли 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.txt/activity .. Дякую за пораду :) .. занадто погані комп’ютери ніколи не розважаються, як трапляються несподівані речі, як випадкове спокушання всіх ваших жінок-колег у facebook :)
AturSams

1
+FollowSymLinksзгадується в документації як обов'язковий для mod_rewriteроботи взагалі з невиразних міркувань безпеки.
Joey

Дві заяви тут хвилюють мене надзвичайно: "Я не дуже добре розуміюсь на всіх можливостях MultiViews, але я знаю, що вона змінює мої правила mod_rewrite, коли активна", і ця "Ця, я не знаю деталей , але я бачив це вже багато разів, тому просто роби це ". Я б хотів, щоб люди, як ви, не писали відповідей на SO про речі, в яких ви не впевнені.
TheCarver

1
@PaparazzoKid: Я думаю, ти помиляєшсь із „Енциклопедією”. Це спільнота людей, які збираються разом, щоб домогтися розуміння технології, з якою вони працюють. На відміну від AW White та Joey перед вами, ваш коментар майже не має значення. MV і FSL - це 2 з багатьох варіантів 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]

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), який не призведе до повторного аналізу вашого запиту.


3

Ще однією чудовою особливістю є перезапис-розширення карт. Вони особливо корисні, якщо у вас є величезна кількість хостів / переписувачів:

Вони схожі на заміну ключа-значення:

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 і RewriteConds для маршрутизації запитів до різних додатків або балансирів завантаження на основі практично будь-якого можливого аспекту запиту просто надзвичайно потужне. Контроль запитів на шляху до бекенда та можливість зміни відповідей на зворотному шляху робить mod_rewrite ідеальним місцем для централізації всіх конфігурацій, пов’язаних з маршрутизацією.

Знайдіть час, щоб його вивчити, воно того варте! :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.