Ссылка: mod_rewrite, перезапись URL и объяснение «красивых ссылок»

144

«Красивые ссылки» - это часто запрашиваемая тема, но она редко получает полное объяснение. mod_rewrite - это один из способов создания "красивых ссылок", но он сложен, а его синтаксис очень краток, его трудно понять, а документация предполагает определенный уровень владения HTTP. Может ли кто-нибудь простыми словами объяснить, как работают "красивые ссылки" и как можно использовать mod_rewrite для их создания?

Другие распространенные имена, псевдонимы, термины для чистых URL-адресов: URL- адреса RESTful , удобные для пользователя URL-адреса, URL-адреса, оптимизированные для SEO , сегментирование и URL-адреса MVC (вероятно, неправильное название)

обмануть
источник
2
Slug или Slugging - еще один распространенный псевдоним / термин для красивых URL-адресов.
Майк Би,
2
@Mike Вроде того, но ярлыки часто являются частью красивых URL-адресов. Слаг довольно специфичен, когда, например, заголовок статьи превращается в удобную для URL форму, которая затем действует как идентификатор этой статьи. Так reference-mod-rewrite-url-rewriting-explainedчто слаг, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedэто красивый URL.
deceze
2
Я думаю , что .htaccessи mod-rewriteметки должны быть обновлены , чтобы включить ссылку на этот вопрос, поскольку он охватывает большую часть того, что предлагается на регулярной основе. Мысли?
Майк Рокетт,

Ответы:

112

Чтобы понять, что такое 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, как предпочтительный способ перезаписи в диспетчере.
Darsstar
80

Чтобы расширить ответ 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

Флаг отсутствия регистра означает, что правило перезаписи нечувствительно к регистру, поэтому для приведенного выше примера правила это будет означать, что оба /blog/1/foo/и /BLOG/1/foo/(или любой его вариант) будут сопоставлены.

L

Последний флаг указывает, что это последнее правило, которое следует обработать. Это означает, что если и только если это правило совпадает, никакие другие правила не будут оцениваться в текущем прогоне обработки перезаписи. Если правило не совпадает, все остальные правила будут проверены в обычном порядке. Если вы не установите этот Lфлаг, все следующие правила будут применены к перезаписанному URL впоследствии.

END

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

QSA

Флаг добавления строки запроса позволяет нам передавать дополнительные переменные по указанному URL-адресу, которые будут добавлены к исходным параметрам get. В нашем примере это означает, что что-то вроде /blog/1/foo/?comments=15загрузится/blog/index.php?id=1&title=foo&comments=15

R

Этот флаг я не использовал в приведенном выше примере, но я подумал, что о нем стоит упомянуть. Это позволяет указать перенаправление http с возможностью включения кода состояния (например R=301). Например, если вы хотите выполнить 301 редирект на / myblog / в / блог /, вы просто напишите правило примерно так:

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

Ссылки

У Stack Overflow есть много других замечательных ресурсов для начала:

И даже удобные для новичков обзоры регулярных выражений:

Часто используемые заполнители

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

Каждый из этих заполнителей обычно заключен в (…)круглые скобки как группа захвата. И весь узор часто в ^………$маркерах начала + конца. Цитирование «паттернов» необязательно.

RewriteRules

Следующие ниже примеры ориентированы на 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
     #                       └────────────────────────────────┘
    

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

  • Слизни с числовыми префиксами
    /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
     #               └───────────────────────────────────┘
    

    Вы можете разделить их на отдельные, RewriteRuleесли это станет слишком сложным.

  • Отправка связанных 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 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Иногда целесообразно чередовать /-делитель и / :или даже .в одном правиле. Или снова используйте два правила RewriteRule, чтобы сопоставить варианты с разными сценариями.

  • Необязательный завершающий /слэш
    /dir=/dir/

    Выбирая пути в стиле каталогов, вы можете сделать его доступным с финальным /

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

    Теперь это обрабатывает оба http://example.com/blog/123и /blog/123/. И этот /?$подход легко добавить к любому другому RewriteRule.

  • Гибкие сегменты для виртуальных путей
    .*/.*/.*/.*

    Большинство правил, с которыми вы столкнетесь, сопоставляют ограниченный набор /…/сегментов пути ресурса с отдельными параметрами GET. Однако некоторые сценарии обрабатывают переменное количество параметров . Механизм регулярных выражений Apache не позволяет опционально использовать произвольное их количество. Но вы можете легко расширить его до блока правил:

     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…

    Как было сказано ранее, вам не всегда нужны слишком общие шаблоны перезаписи. Однако имеет смысл комбинировать статические и конкретные сравнения с .*иногда.

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

    Это опционально /…/…/…делало любые конечные сегменты пути. Что, конечно же, требует, чтобы сценарий обработки разделял их и изменял извлеченные параметры непосредственно (что и делают фреймворки 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-страницы так, чтобы они содержали только красивые ссылки, как показано deceze . Тем временем вы по-прежнему будете получать запросы на старые пути, иногда даже из закладок. В качестве обходного пути вы можете использовать браузеры для пинг-понга для отображения / установки новых 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. В настоящее время пути запросов в RewriteRules полностью зависят от каталога.htaccess . Просто оставьте /вывод.

    · Обратите внимание, что ведущая косая черта все еще верна в <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.

    Это не значит, что вы всегда должны это делать . Content-Negotiation - это просто автоматизм для виртуальных ресурсов.

  • Заказ важен

    См. Все, что вы когда-либо хотели знать о 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">в свой HTML- <head>раздел. Это неявно восстанавливает относительные ссылки к тому, что они были раньше.

    В качестве альтернативы вы можете создать дополнительные правила 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

    • Прежде чем задать свой вопрос

      Как вы, возможно, знаете, Stack Overflow очень подходит для того, чтобы задавать вопросы о 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" или что-то в этом роде. Хотя это полезно для примеров поиска в Google, это устаревшее неправильное употребление.

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

    По идее, «красивые» URL-адреса должны использоваться для того, чтобы сделать веб - сайты удобными для пользователей .

    1. Имея понятные и понятные схемы ресурсов.
    2. Обеспечение долговечности URL-адресов (также известных как постоянные ссылки ).
    3. Обеспечение видимости через /common/tree/nesting.

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

инструменты

Существуют различные онлайн-инструменты для генерации RewriteRules для большинства URL-адресов с параметрами GET:

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

Марио
источник
По-прежнему нужно немного переписать, добавить больше ссылок, а многие подзаголовки несколько неприятны. Здесь есть некоторые совпадения с другими ответами, поэтому, возможно, их можно сократить. Однако в основном речь идет о визуальных примерах и этом списке общих ошибок.
Марио
3
Давно не видел такой красоты ответа! Мои глаза светятся, пока я читаю это. Пожалуйста, не прекращайте публиковать такие ответы :)
Rizier123 08
1
Отличный пост. Я очень быстро понял основные концепции mod_rewrite!
breez
6

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

Многие базовые схемы виртуальных URL-адресов могут быть реализованы без использования RewriteRules. Apache позволяет вызывать сценарии PHP без .phpрасширения и с виртуальным PATH_INFOаргументом.

  1. Используйте PATH_INFO , Люк

    В настоящее время AcceptPathInfo Onчасто включен по умолчанию. Что в основном позволяет .phpURL-адресам и другим ресурсам нести виртуальный аргумент:

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

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

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

  2. Включите MultiViews, чтобы скрыть .phpрасширение

    Самый простой способ также отказаться от .php«расширений файлов» в URL-адресах - это включить:

    Options +MultiViews
    

    Это позволяет Apache выбирать article.phpHTTP-запросы /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_rewriteRewriteRules. Обратите внимание, что большинство из них необходимо настраивать в <VirtualHost>разделе, а не в .htaccessфайлах конфигурации для каждого каталога .

    • ScriptAliasMatchпредназначен в первую очередь для сценариев CGI, но также должен работать и для PHP. Он позволяет использовать регулярные выражения, как и любые другие RewriteRule. Фактически, это, пожалуй, самый надежный вариант настройки универсального переднего контроллера.

    • И простой Aliasпомогает также с несколькими простыми схемами перезаписи.

    • Даже простая ErrorDocumentдиректива может использоваться, чтобы позволить скрипту PHP обрабатывать виртуальные пути. Обратите внимание, что это сложный обходной путь, однако он запрещает все, кроме запросов GET, и по определению заполняет журнал error.log.

    См. Http://httpd.apache.org/docs/2.2/urlmapping.html для получения дополнительных советов.

Марио
источник