Довідка: пояснено mod_rewrite, перезапис URL-адрес та "гарні посилання"


142

"Досить посилання" - це часто запитувана тема, але вона рідко пояснюється повністю. mod_rewrite - це один із способів створення "гарних посилань", але він складний, його синтаксис дуже стислий, важко піддатися крокуванню, а документація передбачає певний рівень володіння HTTP. Чи може хтось просто пояснити, як працюють «гарні посилання» та як mod_rewrite можна використовувати для їх створення?

Інші поширені імена, псевдоніми, терміни для чистих URL-адрес: RESTful URL-адреси, зручні для користувача URL-адреси, SEO- приємні URL-адреси, прослуховування та MVC URL-адреси (можливо, помилковий)


2
Slug або Slugging - ще один поширений псевдонім / термін для досить URL-адрес.
Майк Б

2
@Mike Сортування, але кулі часто є частиною гарних URL-адрес. Слуг досить конкретно, коли, наприклад, заголовок статті перетворюється на зручну для URL-адресу форму, яка потім виступає ідентифікатором цієї статті. Так reference-mod-rewrite-url-rewriting-explainedсамо і слизька, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedє симпатичною URL-адресою.
деге

2
Я думаю, що теги .htaccessта mod-rewriteтеги слід оновлювати, щоб містити посилання на це питання, оскільки воно охоплює значну частину запитань на регулярній основі. Думки?
Майк Рокетт

Відповіді:


110

Щоб зрозуміти, що саме mod_rewrite, спочатку потрібно зрозуміти, як працює веб-сервер. Веб-сервер відповідає на запити HTTP . HTTP-запит на самому базовому рівні виглядає приблизно так:

GET /foo/bar.html HTTP/1.1

Це простий запит браузера на веб-сервер із запитом URL-адреси /foo/bar.html від нього. Важливо підкреслити, що він не вимагає а файл , він вимагає лише якусь довільну URL-адресу. Запит також може виглядати так:

GET /foo/bar?baz=42 HTTP/1.1

Це настільки ж дійсний запит на URL-адресу, і він явно не має нічого спільного з файлами.

Веб-сервер - це програма, яка прослуховує порт, приймає HTTP-запити, що надходять на цей порт, і повертає відповідь. Веб-сервер повністю вільний відповідати на будь-який запит будь-яким способом, який він вважає за потрібне / будь-яким способом, який ви налаштували на відповідь. Ця відповідь не є файлом, це HTTP, яка може мати або не мати нічого спільного з фізичними файлами на будь-якому диску. Веб-серверу не повинно бути Apache, є багато інших веб-серверів, які є лише програмами, які постійно працюють і прикріплені до порту, який відповідає на HTTP-запити. Ви можете написати це самостійно. Цей абзац мав на меті розлучити вас із будь-яким уявленням про те, що URL-адреси безпосередньо відповідають файлам, що насправді важливо зрозуміти. :)

Конфігурація за замовчуванням більшості веб-серверів полягає у пошуку файлу, який відповідає URL-адресі на жорсткому диску. Якщо для кореня документа на сервері встановлено, скажімо,, /var/wwwвін може перевірити, чи /var/www/foo/bar.htmlіснує файл , і чи його так обслуговувати. Якщо файл закінчується ".php", він викликатиме інтерпретатор PHP, а потім повертає результат. Вся ця асоціація повністю настроюється; файл не повинен закінчуватися ".php", щоб веб-сервер запустив його через інтерпретатор PHP, і URL-адреса не повинна відповідати жодному конкретному файлу на диску, щоб щось сталося.

mod_rewrite - це спосіб переписати внутрішню обробку запиту. Коли веб-сервер отримає запит на URL-адресу /foo/bar, ви можете переписати цю URL-адресу в щось інше, перш ніж веб-сервер шукатиме файл на диску, щоб відповідати йому. Простий приклад:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Це правило говорить, коли запит відповідає "/ foo / bar", перепишіть його на "/ foo / baz". Після цього запит буде оброблятися так, як ніби /foo/bazвін був запитаний замість цього. Це можна використовувати для різних ефектів, наприклад:

RewriteRule (.*) $1.html

Це правило відповідає будь-якому ( .*) і захоплює його ( (..)), а потім переписує його, щоб додати ".html". Іншими словами, якщо /foo/barбула запитувана URL-адреса, вона буде оброблятися так, ніби /foo/bar.htmlзапитується. Див. Http://regular-expressions.info для отримання додаткової інформації про регулярне узгодження виразів, захоплення та заміни.

Ще одне часто зустрічається правило:

RewriteRule (.*) index.php?url=$1

Це, знову ж таки, відповідає будь-якому і переписує його у файл index.php з початково запитуваною URL-адресою, доданою в urlпараметр запиту. Тобто для будь-яких запитів, що надходять, виконується файл index.php, і цей файл матиме доступ до оригінального запиту $_GET['url'], тому він може робити все, що завгодно.

В основному ви вводите ці правила перезапису у файл конфігурації веб-сервера . Apache також дозволяє вам * помістити їх у файл, який називається .htaccessв корені документа (тобто поруч із вашими .php-файлами).

* Якщо дозволено первинним файлом конфігурації Apache; це необов’язково, але часто ввімкнено.

Що mod_rewrite не робить

mod_rewrite магічно не робить усі ваші URL-адреси "гарними". Це звичайне непорозуміння. Якщо у вас на цьому веб-сайті є посилання:

<a href="https://stackoverflow.com/my/ugly/link.php?is=not&amp;very=pretty">

нічого mod_rewrite не може зробити, щоб зробити це гарним. Для того, щоб зробити це гарним посиланням, ви повинні:

  1. Змініть посилання на гарне посилання:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Використовуйте mod_rewrite на сервері для обробки запиту до URL, /my/pretty/linkвикористовуючи будь-який із описаних вище способів.

(Можна mod_substituteспільно використовувати для перетворення вихідних HTML-сторінок та їх посилань. Хоча це зазвичай більше зусиль, ніж просто оновлення ваших HTML-ресурсів.)

Існує багато mod_rewrite, які можна створити і дуже складні правила відповідності, які ви можете створити, включаючи ланцюжок декількох переписувань, надання прохання до абсолютно іншого сервісу чи машини, повернення конкретних кодів статусу HTTP як відповідей, перенаправлення запитів тощо. Це дуже потужно і його можна використовувати добре, якщо ви розумієте основний механізм відповіді на запит HTTP. Це автоматично не робить ваші посилання гарними.

Дивіться офіційну документацію для всіх можливих прапорів та опцій.


6
Можливо, згадайте директиву FallbackResource, представлену у версії 2.2.16, як бажаний спосіб переписання на диспетчер.
Дарсстар

78

Щоб розширити відповідь deceze , я хотів надати кілька прикладів та пояснення деяких інших функцій mod_rewrite.

Усі наведені нижче приклади припускають, що ви вже включені RewriteEngine Onу свій .htaccessфайл.

Перепишіть приклад

Розглянемо цей приклад:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

Правило розділено на 4 розділи:

  1. RewriteRule - запускає правило перезапису
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Це називається шаблоном, однак я просто позначаю його як ліву частину правила - те, з чого ви хочете переписати
  3. blog/index.php?id=$1&title=$2 - називається заміна, або права частина правила переписання - те, що ви хочете переписати
  4. [NC,L,QSA] - це прапори для правила переписання, розділені комою, про які я поясню пізніше

Вищеописане перезапис дозволить вам зв’язати щось подібне, /blog/1/foo/і воно фактично завантажиться /blog/index.php?id=1&title=foo.

Ліва частина правила

  • ^вказує початок назви сторінки - тому вона буде переписана, example.com/blog/...але ніexample.com/foo/blog/...
  • Кожен набір (…)дужок являє собою регулярний вираз, який ми можемо фіксувати як змінну в правій частині правила. У цьому прикладі:
    • Перший набір дужок - ([0-9]+) - відповідає рядку довжиною не менше 1 символу та лише числовими значеннями (тобто 0-9). На це можна посилатися $1в правій частині правила
    • Другий набір дужок відповідає рядку довжиною не менше 1 символу, що містить лише буквено-цифрові символи (AZ, az, або 0-9) або -або +(примітка +відхиляється зворотною косою рисою, оскільки без її уникнення це буде виконуватися як регулярний вираз персонаж повторення ). На це можна посилатися$2 в правій частині правила
  • ? означає, що попередній символ необов’язковий, тому в цьому випадку і те, і інше /blog/1/foo/ і /blog/1/fooпереписуються на одне і те ж місце
  • $ вказує, що це кінець рядка, з яким ми хочемо відповідати

Прапори

Це варіанти, які додаються у квадратні дужки в кінці правила перезапису, щоб вказати певні умови. Знову ж таки, існує багато різних прапорів, про які ви можете прочитати в документації , але я перегляну деякі більш поширені прапори:

NC

Прапор no case означає, що правило перезапису є нечутливим до регістру, тому для прикладу, наведеного вище, це означає, що обидва /blog/1/foo/та /BLOG/1/foo/(або будь-які їх зміни) будуть збігатися.

L

Останній прапор вказує, що це останнє правило, яке слід обробити. Це означає, що якщо і лише якщо це правило відповідає, додаткові правила не будуть оцінені в поточному циклі обробки перезапису. Якщо правило не відповідає, всі інші правила будуть випробувані в порядку, як зазвичай. Якщо ви не встановите Lпрапор, до переписаної URL-адреси згодом будуть застосовані всі наступні правила .

END

Оскільки Apache 2.4 ви можете також використовувати [END]прапор. Правило відповідності з ним повністю припинить подальшу обробку псевдоніму / перезапису. (Тоді як [L]прапор може часто викликати другий раунд, наприклад, при переписуванні у підкаталоги або з нього.)

QSA

Прапор додавання рядка запиту дозволяє нам передати додаткові змінні до вказаної URL-адреси, яка буде додана до вихідних параметрів отримання. Для нашого прикладу це означає, що щось подібне /blog/1/foo/?comments=15завантажиться/blog/index.php?id=1&title=foo&comments=15

R

Цей прапор - це не той, який я використовував у наведеному вище прикладі, але це той, який я вважав, що варто згадати. Це дозволяє вказати переспрямування http, з можливістю включити код статусу (наприклад R=301). Наприклад, якщо ви хочете зробити переадресацію 301 на / myblog / на / blog /, ви просто напишіть правило, подібне до цього:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

Перепишіть умови

Умови перезапису роблять переписування ще більш потужними, дозволяючи задавати переписування для більш конкретних ситуацій. Існує маса умов, про які можна прочитати в документації , але я торкнуся декількох поширених прикладів і поясню їх:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Це дуже поширена практика, яка надасть вам домен www.(якщо його вже немає) та виконає переспрямування 301. Наприклад, завантаження http://example.com/blog/його перенаправить вас наhttp://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

Це дещо рідше, але це хороший приклад правила, яке не виконується, якщо ім'я файлу - це каталог або файл, який існує на сервері.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] виконає перезапис лише для файлів із розширенням файлу jpg, jpeg, gif чи png (нечутливий до регістру).
  • %{REQUEST_FILENAME} !-f перевірить, чи існує файл на поточному сервері, і виконає перезапис лише, якщо цього немає
  • %{REQUEST_FILENAME} !-d перевірить, чи існує файл на поточному сервері, і виконає перезапис лише, якщо цього немає
  • Перезапис спробує завантажити той самий файл на інший домен

39

Список літератури

Переповнення стека має багато інших чудових ресурсів для початку роботи:

І навіть нові огляди регексу:

Застосовувані заповнювачі

  • .*відповідає що завгодно, навіть порожній рядок. Ви не хочете використовувати цю схему скрізь, але часто в останньому правилі резервного копіювання.
  • [^/]+частіше використовується для сегментів шляху. Він відповідає будь-якому, окрім нахилу вперед.
  • \d+ відповідає лише числовим рядкам.
  • \w+відповідає буквено-цифровим символам. В основному це скорочення [A-Za-z0-9_].
  • [\w\-]+для сегментів шляху "slug", використовуючи літери, цифри, тире - та _
  • [\w\-.,]+додає періоди та коми. Віддайте перевагу втеченому \-тире в […]шарах.
  • \.позначає буквальний період. В іншому випадку .поза межами […]заповнення будь-якого символу.

Кожен із цих заповнювачів зазвичай загортається в (…)дужки як група захоплення. І весь візерунок часто ^………$стартує + кінцевими маркерами. Цитування "шаблонів" необов'язково.

Перепишіть правила

Наведені нижче приклади орієнтовані на PHP та трохи більше, тому їх легко адаптувати для подібних випадків. Вони просто підсумки, часто посилаються на більше варіантів або детальні запитання та відповіді.

  • Статичне відображення
    /contact,/about

    Скорочення декількох імен сторінок до внутрішніх файлових схем найпростіше:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Числові ідентифікатори
    /object/123

    Введення ярликів, як http://example.com/article/531до існуючих скриптів PHP, також легко. Числовий заповнювач просто може бути перекомпонований до $_GETпараметра:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Застібки в стилі слизькі
    /article/with-some-title-slug

    Ви можете легко розширити це правило, щоб дозволити /article/title-stringзаповнювачам:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    Зауважте, що ваш сценарій повинен бути в змозі (або адаптований) для відображення цих заголовків назад до ідентифікаторів баз даних. Самі перепишіть правила не можуть створювати та не вгадувати інформацію з повітря.

  • Сліпи з числовими префіксами
    /readable/123-plus-title

    Тому ви часто бачите змішані /article/529-title-slugшляхи, які використовуються на практиці:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    Тепер ви можете просто пропустити проходження title=$2, так як ваш сценарій, як правило, покладається на ідентифікатор бази даних у будь-якому випадку. Це -title-slugстало довільною прикрасою URL-адрес.

  • Уніфікованість з альтернативними списками
    /foo/… /bar/… /baz/…

    Якщо у вас є подібні правила для декількох віртуальних контурів до сторінок, ви можете їх зіставити та ущільнити |альтернативними списками. І знову просто переставте їх на внутрішні параметри GET:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    Ви можете розділити їх на окремі RewriteRules, якщо це стане занадто складним.

  • Відправлення пов'язаних URL-адрес на різні бази даних
    /date/SWITCH/backend

    Більш практичним використанням альтернативних списків є зіставлення запитів шляхів до різних скриптів. Наприклад, надати уніфіковані URL-адреси для старшої та новішої веб-програми на основі дат:

     #                   ┌─────────────────────────────┐
     #                   │                 ┌───────────┼───────────────┐
     RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
     RewriteRule ^blog/(\d+)/([\d-]+)/?$  modern/blog/index.php?start=$2
     #                          └──────────────────────────────────────┘
    

    Це просто переставляє повідомлення 2009-2011 на один сценарій, а всі інші роки неявно іншому оброблювачу. Зверніть увагу на більш конкретні правила, що підходять першими . Кожен сценарій може використовувати різні параметри GET.

  • Інші роздільники, ніж просто /косі риски
    /user-123-name

    Ви найчастіше бачите RewriteRules, щоб імітувати структуру віртуального каталогу. Але ти не змушений бути нетворчим. Ви також можете використовувати -дефіси для сегментації або структури.

     RewriteRule ^user-(\d+)$    show.php?what=user&id=$1
     #                   └──────────────────────────────┘
     # This could use `(\w+)` alternatively for user names instead of ids.
    

    Для також поширеної /wiki:section:Page_Nameсхеми:

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Іноді він підходить для перемикання між /-delimiters і :чи .в тому ж правилі навіть. Або знову два RewriteRules, щоб відобразити варіанти на різні сценарії.

  • Необов’язковий кінець /косої риски
    /dir=/dir/

    Вибираючи контури в стилі каталогів, ви можете зробити його доступним і без остаточного /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    Тепер це справляється і з http://example.com/blog/123і /blog/123/. І /?$підхід легко додати до будь-якої іншої RewriteRule.

  • Гнучкі сегменти для віртуальних шляхів
    .*/.*/.*/.*

    Більшість правил, з якими ви зіткнетесь, відображає обмежений набір /…/сегментів шляху ресурсів до окремих параметрів GET. Однак деякі сценарії обробляють різну кількість варіантів . Двигун Ageche regexp не дозволяє оптимізувати довільну їх кількість. Але ви можете легко розширити його в блок правил самостійно:

     Rewriterule ^(\w+)/?$                in.php?a=$1
     Rewriterule ^(\w+)/(\w+)/?$          in.php?a=$1&b=$2
     Rewriterule ^(\w+)/(\w+)/(\w+)/?$    in.php?a=$1&b=$2&c=$3
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Якщо вам потрібно до п'яти сегментів шляху, скопіюйте цю схему разом з п'яти правил. Звичайно, ви можете використовувати більш конкретний [^/]+заповнювач кожного. Тут замовлення не так важливе, як і не перетинається. Тож добре спочатку використовувати найбільш часто використовувані шляхи.

    Крім того, ви можете використовувати параметри масиву PHP за допомогою ?p[]=$1&p[]=$2&p[]=3рядка запиту тут - якщо ваш сценарій віддає перевагу їх попередньому розбиттю . (Хоча звичайніше просто використовувати загальноприйняте правило, і дозволити самому сценарію розширювати сегменти з REQUEST_URI.)

    Дивіться також: Як я можу перетворити сегменти мого URL-адреси в пари рядка запиту рядок ключ-значення?

  • Необов’язкові сегменти
    prefix/opt?/.*

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

      RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$  ?main=$1&opt=$2&suffix=$3
    

    Тепер складніший малюнок (?:/([^/])+)?просто загортає групу, яка не захоплює (?:…) , і робить її необов'язковою )?. Вміщений заповнювач([^/]+) буде схемою заміни $2, але будь порожнім, якщо середнього /…/шляху немає .

  • Захопіть залишок
    /prefix/123-capture/…/*/…whatever…

    Як було сказано раніше, ви не часто хочете занадто загальні шаблони перезапису. Однак має сенс поєднувати статичні та конкретні порівняння з a.* часом.

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    Це оптимізувало будь-які /…/…/…сегменти шляху. Тоді, звичайно, потрібен сценарій обробки, щоб розділити їх, а сам змінний ify витягнути параметри (це що роблять рамки Web- "MVC" ).

  • Останній файл "розширення"
    /old/path.HTML

    В URL-адресах насправді не є розширення файлів. Про що йдеться у всій цій довідці (= URL-адреси - це віртуальні локатори, не обов'язково зображення прямої файлової системи) Однак якщо раніше у вас було зіставлення файлів 1: 1, ви можете скласти більш прості правила:

     RewriteRule  ^styles/([\w\.\-]+)\.css$  sass-cache.php?old_fn_base=$1
     RewriteRule  ^images/([\w\.\-]+)\.gif$  png-converter.php?load_from=$2
    

    Іншими поширеними способами є перезапис застарілих .htmlшляхів до новіших .phpобробників або просто псевдонім імен каталогів лише для окремих (фактичних / реальних) файлів.

  • Пінг-Понг (переспрямовує та переписує в унісон)
    /ugly.html← →/pretty

    Тож у якийсь момент ви переписуєте свої HTML-сторінки, щоб вони містили лише гарні посилання, як це окреслено дезе . Тим часом ви все одно отримаєте запити для старих шляхи, іноді навіть із закладок. Як вирішення , ви можете переглядати пінг-понг для відображення / встановлення нових URL-адрес.

    Цей поширений трюк передбачає надсилання переадресації 30x / Location кожного разу, коли вхідна URL-адреса слідує за застарілою / потворною схемою іменування. Потім веб-переглядачі запросять новий / гарний URL-адресу, який згодом буде переписаний (лише внутрішньо) на початкове або нове місце.

     # redirect browser for old/ugly incoming paths
     RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
    
     # internally remap already-pretty incoming request
     RewriteRule ^teams$ teams.php        [QSA,END]
    

    Зверніть увагу, як цей приклад просто використовується [END]замість [L]безпечного чергування. Для старих версій Apache 2.2 ви можете використовувати інші обхідні шляхи, крім того, як перенастроювати параметри рядка запиту, наприклад: Перенаправляти некрасивий на гарний URL, перевстановлювати назад на некрасивий шлях, без нескінченних циклів

  • Пробіли у візерунках
    /this+that+

    Це не так сильно в адресних панелях браузера, але ви можете використовувати пробіли в URL-адресах. Для перезапису шаблонів використовуйте \␣пробіли, пропущені косою рисою . Ще просто "-процитуйте весь візерунок або заміну:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    Клієнти серіалізують URL-адреси з пробілами +або %20для них. Однак у RewriteRules вони інтерпретуються буквальними символами для всіх відносних сегментів шляху.

Часті копії:

Поширені .htaccessпідводні камені

Тепер візьміть це з зерном солі. Не кожна порада може бути узагальнена у всіх контекстах. Це лише простий підсумок відомих та декількох непомітних каменів спотикання:

  • Увімкнути mod_rewriteі.htaccess

    Щоб реально використовувати RewriteRules у файлах конфігурації для кожного каталогу, ви повинні:

    • Перевірте, чи AllowOverride Allувімкнено ваш сервер . В іншому випадку ваші .htaccessдирективи щодо каталогу будуть ігноруватися, і RewriteRules не працюватиме.

    • Очевидно , mod_rewriteувімкнено у httpd.confрозділі модулів.

    • Додайте до кожного списку правил RewriteEngine Onнерухомі. Хоча mod_rewrite неявно активний у розділах <VirtualHost>та <Directory>розділах, .htaccessфайли за каталогами потребують його індивідуального виклику.

  • Провідна коса риса ^/не збігається

    Не слід запускати свої .htaccessшаблони RewriteRule ^/звичайно:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Це часто можна побачити в старих навчальних посібниках. Це було правильним для давніх версій Apache 1.x. На сьогодні шляхи запиту зручно повністю відносно каталогів у .htaccessRewriteRules. Просто залиште провідних /.

    · Зверніть увагу, що провідна коса риса все ще правильна в <VirtualHost>розділах. Ось чому ви часто бачите його ^/?оптимізованим для паритету правил.
    · Або при використанні RewriteCond %{REQUEST_URI}ви все одно збігаєтеся за ведучого /.
    · Див. Також Webmaster.SE: Коли потрібна провідна коса риса (/) у шаблонах mod_rewrite?

  • <IfModule *> обгортки пішли!

    Напевно, ви це бачили на багатьох прикладах:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Це має сенс у <VirtualHost>розділах - якщо він поєднувався з іншим резервним варіантом, таким як ScriptAliasMatch. (Але ніхто ніколи цього не робить).
    • І зазвичай він розповсюджується для .htaccessнаборів правил за замовчуванням для багатьох проектів з відкритим кодом. Там це просто позначається як резервний, і підтримує "потворні" URL-адреси як стандартні.

    Однак цього зазвичай не хочеться у власних .htaccessфайлах.

    • По-перше, mod_rewrite не випадково відключається. (Якби це сталося, у вас виникли б більші проблеми).
    • Якби це дійсно було відключено, ваші RewriteRules все одно не працюватимуть.
    • Він призначений для запобігання 500помилок HTTP . Зазвичай це досягає отримання вами користувачів 404помилок HTTP . (Не так вже й зручніше в користуванні, якщо задуматися.)
    • Практично це просто пригнічує більш корисні записи журналу або повідомлення про сервер. Ви б не були мудрішими , чому ваші RewriteRules ніколи не працюють.

    Те, що здається привабливим як узагальнена гарантія, часто виявляється перешкодою на практиці.

  • Не використовуйте RewriteBase якщо не потрібно

    Багато прикладів копіювання + вставки містять RewriteBase / директиву. Яке як і раніше є неявним за замовчуванням. Тож вам насправді цього не потрібно. Це рішення для фантазійних схем переписування VirtualHost та помилкових шляхів DOCUMENT_ROOT для деяких спільних хостерів.

    Має сенс використовувати з окремими веб-додатками в більш глибоких підкаталогах. Це може скоротити шаблони RewriteRule в таких випадках. Як правило, краще віддати перевагу відносним специфікаторам шляху в наборах правил для каталогу.

    Дивіться також, як працює RewriteBase в .htaccess

  • Вимкнути, MultiViewsколи віртуальні шляхи перетинаються

    Перезапис URL-адрес використовується в основному для підтримки віртуальних вхідних шляхів. Зазвичай ви просто один диспетчерський скрипт ( index.php) або кілька окремих оброблювачів ( articles.php, blog.php, wiki.php...). Останній може зіткнутися з аналогічними віртуальними шляхами RewriteRule.

    Запит, /article/123наприклад, може відображатися неявно article.phpз /123PATH_INFO. Вам або доведеться захищати свої правила за допомогою звичайного RewriteCond !-f+ !-dта / або відключати підтримку PATH_INFO, або, можливо, просто відключати Options -MultiViews.

    Що не означає, що завжди потрібно . Зміст-переговори - це лише автоматизм віртуальних ресурсів.

  • Порядок важливий

    Дивіться все, що ви коли-небудь хотіли знати про mod_rewrite, якщо цього ще не зробили. Об'єднання декількох RewriteRules часто призводить до взаємодії. Це не те, що звично перешкоджає кожному [L]прапору, але схема, яку ви будете приймати колись розбиратися. Ви можете повторно повторно повторно записи віртуальних шляхів від одного правила до іншого, поки не досягне фактичного цільового обробника.

    Тим не менш, ви часто хочете, щоб у ранніх правилах були найбільш специфічні правила (фіксовані рядкові /forum/…шаблони або більш обмежені заставки [^/.]+) . Загальні правила "slurp-all" ( ) краще залишити пізнішим . (Виняток - захист як основний блок.).*RewriteCond -f/-d

  • Таблиці стилів та зображення перестають працювати

    Коли ви вводите структури віртуальних каталогів, /blog/article/123це впливає на відносні посилання на ресурси в HTML (наприклад, <img src=mouse.png>). Що можна вирішити:

    • Використовуючи лише абсолютні посилання на сервер href="https://stackoverflow.com/old.html"абоsrc="/logo.png"
    • Часто просто додаючи <base href="https://stackoverflow.com/index">у свій <head>розділ HTML . Це неявно відновлює відносні посилання на те, що вони були раніше.

    Ви також можете створити додаткові RewriteRules для повторного зв’язування .cssабо проходження .pngмаршрутів до їх початкових місць. Але це як зайве, так і додаткові перенаправлення та перешкоджає кешування.

    Дивіться також: CSS, JS та зображення не відображаються з досить URL-адресою

  • RewriteConds просто маскує одну RewriteRule

    Поширене неправильне тлумачення полягає в тому, що RewriteCond блокує кілька RewriteRules (оскільки вони візуально розташовані разом):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    Що не за замовчуванням. Ви можете ланцюг, використовуючи [S=2]прапор. Ще вам доведеться повторити їх. Хоча іноді ви можете створити "перевернуте" основне правило, щоб [END] рано переписати обробку.

  • QUERY_STRING звільнено від RewriteRules

    Ви не можете зіставити RewriteRule index.php\?x=y, оскільки mod_rewrite порівнює лише відносні шляхи за замовчуванням. Однак ви можете зіставити їх окремо через:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    Див. Також Як я можу співставити змінні рядки запиту з mod_rewrite?

  • .htaccess vs. <VirtualHost>

    Якщо ви використовуєте RewriteRules у конфігураційному файлі за каталогом, то турбуватися про ефективність регулярних виразів безглуздо. Apache зберігає складені шаблони PCRE довше, ніж процес PHP, із загальною рамкою маршрутизації. Однак для сайтів з високим трафіком слід враховувати переміщення наборів правил у конфігурацію сервера vhost, після того, як вони пройшли битву.

    У цьому випадку віддавайте перевагу ^/?префіксу розділеного каталога на основі опціонованого. Це дозволяє вільно переміщувати RewriteRules між файлами конфігурації PerDir та сервером.

  • Кожного разу, коли щось не працює

    Не біда.

    • Порівняйте access.logіerror.log

      Часто ви можете зрозуміти, як RewriteRule погано поводиться лише з огляду на ваше error.logі access.log. Порівнюйте часи доступу, щоб побачити, який шлях запиту спочатку входив, і який шлях / файл Apache не зміг вирішити (помилка 404/500).

      Це не говорить вам про те, який RewriteRule є винуватцем. Але недоступні кінцеві шляхи, як-от, /docroot/21-.itle?index.phpможуть дати можливість інспектувати далі. В іншому випадку відключіть правила, поки не отримаєте деякі передбачувані шляхи.

    • Увімкніть RewriteLog

      Див. Документи Apache RewriteLog . Для налагодження ви можете ввімкнути це у розділах vhost:

      # Apache 2.2
      RewriteLogLevel 5
      RewriteLog /tmp/rewrite.log
      
      # Apache 2.4
      LogLevel alert rewrite:trace5
      #ErrorLog /tmp/rewrite.log
      

      Це дає детальний підсумок того, як модифікуються шляхи вхідних запитів змінюються кожним правилом:

      [..] applying pattern '^test_.*$' to uri 'index.php'
      [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
      [..] applying pattern '^index\.php$' to uri 'index.php'
      

      Що допомагає звузити надто загальні правила та повторно виражати випадкові випадки.

      Дивіться також:
      · .htaccess не працює (mod_rewrite)
      · Поради щодо налагодження .htaccess правила перезапису

    • Перш ніж задавати власне запитання

      Як ви можете знати, переповнення стека дуже підходить для запитань на mod_rewrite. Складіть їх на тему , включивши попередні дослідження та спроби (уникайте зайвих відповідей), продемонструйте основні розуміння та:

      • Додайте повні приклади вхідних URL-адрес, хибно переписані цільові шляхи, вашу реальну структуру каталогів.
      • Повний набір RewriteRule, але також виділення імовірного несправного.
      • Версії Apache та PHP, тип ОС, файлова система, DOCUMENT_ROOT та $_SERVERсередовище PHP, якщо мова йде про невідповідність параметрів.
      • Уривок з вашої програми access.logта error.logперевірити, до чого вирішено діючі правила. Ще краще, rewrite.logпідсумок.

      Це дає швидші та точніші відповіді та робить їх кориснішими для інших.

  • Прокоментуйте своє .htaccess

    Якщо ви копіюєте приклади звідкись, не забудьте включити # comment and origin link. Хоча пропускати атрибуцію просто погано, але це дуже часто шкодить технічному обслуговуванню. Документуйте будь-який код або джерело підручника. Зокрема, неперевершені, ви повинні бути ще більше зацікавлені у тому, щоб не ставитися до них, як до магічних чорних ящиків.

  • Це не "SEO" -URL

    Відмова від відповідальності: Лише домашня тварина. Ви часто чуєте гарні схеми переписування URL-адрес, які називаються посиланнями "SEO" або щось подібне. Хоча це корисно для прикладів googling, це датоване неправильне значення.

    Жоден із сучасних пошукових систем насправді не турбує сегменти .htmlта .phpшляхи чи ?id=123рядки запитів. Пошукові системи старих, таких як AltaVista, зробив уникали сканування веб-сайтів з потенційно амбітними шляхами доступу. Сучасні сканери часто навіть жадають глибоких веб-ресурсів.

    Те, що "гарненькі" URL-адреси слід використовувати, це робить веб - сайти зручними для користувачів .

    1. Маючи читані та очевидні схеми ресурсів.
    2. Переконайтеся, що URL-адреси довговічні (постійна посилання AKA ).
    3. Забезпечення зрозумілості через /common/tree/nesting.

    Однак не жертвуйте унікальними вимогами до конформізму.

Інструменти

Існують різні онлайн-інструменти для створення RewriteRules для більшості GET-параметрів URL:

В основному виводяться [^/]+загальні заповнювачі, але, ймовірно, достатньо для тривіальних сайтів.


Ще потрібно трохи переписати, більше посилань, і багато підзаголовків дещо неприємні. Тут є деякі дублювання з іншими відповідями, тому їх можна скоротити. В основному, це стосується візуальних прикладів, а також цього списку загальних хатчей.
Маріо

3
Давно не бачили такої краси відповіді! Мої очі світяться, коли я це читаю. Будь ласка, не припиняйте публікувати такі відповіді :)
Rizier123

1
Відмінна посада. Змусив мене зрозуміти основні поняття mod_rewrite дуже швидко!
вітер

6

Альтернативи mod_rewrite

Безліч основних віртуальних схем URL-адрес можна досягти без використання RewriteRules. Apache дозволяє викликати сценарії PHP без .phpрозширення та з віртуальним PATH_INFOаргументом.

  1. Використовуйте PATH_INFO , Лука

    На сьогоднішній день AcceptPathInfo Onчасто за умовчанням увімкнено. Що в основному дозволяє .phpі іншим URL-адресам ресурсів нести віртуальний аргумент:

    http://example.com/script.php/virtual/path
    

    Тепер це /virtual/pathвідображається в PHP як $_SERVER["PATH_INFO"]там, де ви можете обробляти будь-які додаткові аргументи, як завгодно.

    Це не так зручно , як такі, що Apache сегменти окремий вхід в шлях $1, $2, $3і передачі їх в якості окремих $_GETзмінних в РНР. Це просто емуляція "гарних URL-адрес" з меншими зусиллями налаштування.

  2. Увімкніть MultiView, щоб приховати .phpрозширення

    Найпростіший варіант також відхилити .php"розширення файлів" у URL-адресах:

    Options +MultiViews
    

    Це означає, що Apache select article.phpдля HTTP-запитів увімкнено /articleчерез відповідне базове ім'я. І це добре працює разом із вищезгаданою функцією PATH_INFO. Тож ви можете просто використовувати такі URL-адресиhttp://example.com/article/virtual/title . Що має сенс, якщо у вас є традиційна веб-програма з декількома точками / сценаріями виклику PHP.

    Зауважте, що MultiViews має іншу / більш широку мету. Це вимагає дуже незначного покарання за продуктивність, оскільки Apache завжди шукає інші файли з відповідними базовими іменами. Це фактично означало для Content-Переговорів , тому браузери отримують кращий варіант серед наявних ресурсів (наприклад article.en.php, article.fr.php, article.jp.mp4).

  3. SetType або SetHandler для .phpсценаріїв без розширень

    Більш спрямований підхід, щоб уникнути перенесення .phpсуфіксів у URL-адресах, - це налаштування обробника PHP для інших файлових схем. Найпростішим варіантом є переопределення типового MIME / типу обробника за допомогою .htaccess:

    DefaultType application/x-httpd-php
    

    Таким чином, ви можете просто перейменувати свій article.phpскрипт на просто article(без розширення), але все-таки обробити його як сценарій PHP.

    Тепер це може мати певні наслідки для безпеки та продуктивності, тому що всі файли без розширень проходять через PHP. Тому ви можете альтернативно встановити таку поведінку лише для окремих файлів:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    Це дещо залежить від налаштування вашого сервера та використовуваного PHP SAPI. Поширені альтернативи включають ForceType application/x-httpd-phpабо AddHandler php5-script.

    Знову зауважте, що такі налаштування поширюються від однієї .htaccessдо підпапок. Ви завжди повинні вимкнути виконання сценарію ( SetHandler Noneта Options -Execабо php_flag engine offін.) Для статичних ресурсів та завантажувати / каталоги тощо.

  4. Інші схеми переписування Apache

    Серед безлічі варіантів Apache пропонує mod_aliasфункції, які іноді працюють так само добре, як mod_rewriteі RewriteRules. Зауважте, що більшість із них повинні бути налаштовані в <VirtualHost>розділі, а не в .htaccessконфігураційних файлах за каталогами .

    • ScriptAliasMatchнасамперед для сценаріїв CGI, але також повинен працювати для PHP. Це дозволяє регулярні вирівнювання, як і будь-яке RewriteRule. Насправді це, мабуть, найбільш надійний варіант конфігурувати передній контролер.

    • І звичайна Aliasдопомога також у кількох простих схемах переписування.

    • Навіть звичайна ErrorDocumentдиректива може бути використана для дозволу скрипту PHP обробляти віртуальні шляхи. Зауважте, що це хитке вирішення, проте забороняє будь-що, крім запитів GET, та заповнює error.log за визначенням.

    Щоб отримати подальші поради, див. Http://httpd.apache.org/docs/2.2/urlmapping.html .

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