Ссылка: mod_rewrite, переписывание URL и "красивые ссылки" объяснены

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

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

Ответ 1

Чтобы понять, что вам нужно знать о 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="/my/ugly/link.php?is=not&amp;very=pretty">

там ничего не может сделать mod_rewrite, чтобы сделать это красивым. Чтобы сделать это красивой ссылкой, вы должны:

  1. Измените ссылку на красивую ссылку:

    <a href="/my/pretty/link">
    
  2. Используйте mod_rewrite на сервере для обработки запроса URL /my/pretty/link используя любой из описанных выше способов.

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

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

См. Официальную документацию для всех возможных флагов и опций.

Ответ 2

Чтобы расширить отменить ответ, я хотел бы привести несколько примеров и объяснений некоторых других функций mod_rewrite.

Все приведенные ниже примеры предполагают, что вы уже включили RewriteEngine On в свой .htaccess файл.

Пример перезаписи

Давайте рассмотрим этот пример:

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

Правило разбивается на 4 раздела:

  • RewriteRule - запускает правило перезаписи
  • ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Это называется шаблоном, однако я просто буду рассматривать его как левую сторону правила - то, что вы хотите переписать из
  • blog/index.php?id=$1&title=$2 - называется подстановкой или правой частью правила перезаписи - то, что вы хотите переписать на
  • [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

Флаг case не означает, что правило перезаписи нечувствительно к регистру, поэтому для приведенного выше правила примера это будет означать, что оба /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/to/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 проверит, существует ли файл на текущем сервере и выполняется только переписывание, если оно не
  • Переписывание попытается загрузить тот же файл в другой домен

Ответ 3

Ссылки

Для есть много других полезных ресурсов:

И новые регулярные выражения, доступные для новичков:

Использованные заполнители

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

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

RewriteRules

Следующие примеры являются PHP-ориентированными и немного более инкрементальными, легче адаптироваться для подобных случаев. Они просто сводки, часто ссылаются на большее количество вариантов или подробных Q & As.

  • Статическое отображение /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
     #                      └───────────────────────────┘
    
  • Заполнитель в стиле Slug
    /article/with-some-title-slug

    Вы можете легко расширить это правило, чтобы использовать /article/title-string заполнители:

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

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

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

    Поэтому вы часто увидите смешанные пути /article/529-title-slug, используемые на практике:

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

    Теперь вы можете просто пропустить передачу title=$2 в любом случае, потому что ваш script обычно будет полагаться на идентификатор базы данных. -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 гг. на один script и все другие годы неявным образом на другой обработчик. Обратите внимание на более конкретное правило. Каждый script может использовать разные параметры 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 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Иногда он подходит для чередования между / -пределителями и : или . в том же правиле. Или еще два RewriteRules для сопоставления вариантов на разные скрипты.

  • Дополнительный трейлинг / slash
    /dir= /dir/

    При выборе путей в стиле каталога вы можете сделать это доступным с и без окончательного /

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

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

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

    Большинство правил, с которыми вы столкнетесь, сопоставляете ограниченный набор сегментов пути /…/ к отдельным параметрам GET. Однако некоторые скрипты обрабатывают переменное количество опций. Механизм regexp от 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
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Если вам нужно до пяти сегментов пути, скопируйте эту схему в пять правил. Вы можете, конечно, использовать более конкретный [^/]+ placeholder каждый. Здесь порядок не так важен, как и не перекрывается. Таким образом, использование наиболее часто используемых путей в порядке.

    В качестве альтернативы вы можете использовать параметры массива PHP через строку запроса ?p[]=$1&p[]=$2&p[]=3 здесь, если ваш script просто предпочитает их предварительно разделять. (Хотя чаще всего используется правило catch-all, и сам script расширяет сегменты из REQUEST_URI.)

    См. также: Как преобразовать сегменты URL-адреса в строки ключа-строки запроса?

  • Дополнительные сегменты prefix/opt?/.*

    Общим вариантом является наличие необязательных префиксов внутри правила. Это обычно имеет смысл, если у вас есть статические строки или более ограниченные заполнители:

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

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

  • Захват остатка
    /prefix/123-capture/…/*/…whatever…

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

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

    Это опционально для любых сегментов трейлинга пути /…/…/…. Который, конечно, требует обработки script, чтобы разделить их, и параметры, измененные varabl-ify (это то, что делают веб-структуры 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 или просто псевдонимов имен каталогов только для отдельных (реальных/реальных) файлов.

  • Ping-Pong (переадресация и переписывание в унисон)
    /ugly.html ← → /pretty

    Итак, в какой-то момент вы переписываете свои HTML-страницы для переноса только хороших ссылок, как обозначенных путем deceze. Тем временем вы будете получать запросы на старые пути, иногда даже из закладок. В качестве обходного пути вы можете использовать браузеры ping-pong для отображения/установки новые URL-адреса.

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

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

  • <IfModule *> wrappers begone!

    Вероятно, вы видели это во многих примерах:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Это имеет смысл в разделах <VirtualHost> - если оно было объединено с другим вариантом резервного копирования, например ScriptAliasMatch. (Но никто никогда этого не делает).
    • И он обычно распространяется для стандартных наборов правил .htaccess со многими проектами с открытым исходным кодом. Там это просто означает резервную копию и сохраняет "уродливые" URL-адреса по умолчанию.

    Однако вы не хотите, обычно в ваших собственных файлах .htaccess.

    • Во-первых, mod_rewrite не случайно отключается. (Если бы это произошло, у вас были бы большие проблемы).
    • Если бы это действительно было отключено, RewriteRules все равно не сработает.
    • Он предназначен для предотвращения ошибок HTTP 500. То, что он обычно выполняет, это украсить ваших пользователей с ошибками HTTP 404. (Не намного удобнее, если вы думаете об этом.)
    • Практически это просто подавляет более полезные записи журнала или почтовые сообщения для сервера. Вы бы не мудрее, почему ваши RewriteRules никогда не работают.

    То, что кажется заманчивым, как обобщенная защита, часто оказывается препятствием на практике.

  • Не используйте RewriteBase, если это необходимо

    Многие примеры copy + paste содержат директиву RewriteBase /. Во всяком случае, это неявный по умолчанию. Поэтому вам это действительно не нужно. Это обходное решение для причудливых схем перезаписи VirtualHost и ошибочные пути DOCUMENT_ROOT для некоторых общих хостеров.

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

    См. также Как работает RewriteBase в .htaccess

  • Отключить MultiViews при перекрытии виртуальных путей

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

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

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

  • Важное значение заказа

    Смотрите Все, что вы хотели узнать о mod_rewrite если вы еще этого не сделали. Объединение нескольких RewriteRules часто приводит к взаимодействию. Это не то, что обычно предотвращает флаг [L], а схему, которую вы когда-нибудь примете. Вы можете повторно переписывать виртуальные пути от одного правила к другому, пока не достигнете фактического обработчика цели.

    Тем не менее, вы часто хотите иметь наиболее конкретные правила (фиксированные строки /forum/… шаблоны или более ограничительные заполнители [^/.]+) в ранних правилах. Общие правила slurp-all (.*) лучше оставлять на более поздние. (Исключением является RewriteCond -f/-d guard в качестве основного блока.)

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

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

    • Только использование абсолютных ссылок на сервер href="/old.html" или src="/logo.png"
    • Часто просто добавляя <base href="/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 и сервером config.

  • Всякий раз, когда что-то не работает

    Не волнуйся.

    • Сравните access.log и error.log

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

      Это не говорит вам, какой RewriteRule является виновником. Но недоступные конечные пути, такие как /docroot/21-.itle?index.php, могут дать информацию о том, где еще можно проверить. В противном случае отключите правила, пока не получите некоторые предсказуемые пути.

    • Включить RewriteLog

      См. Apache RewriteLog docs. Для отладки вы можете включить его в разделах 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 переписать правила

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

      Как вы, возможно, знаете, Qaru очень подходит для вопросов по mod_rewrite. Сделайте их on-topic включив предварительные исследования и попытки (избегайте избыточных ответов), продемонстрируйте основные и:

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

      Это устанавливает более быстрые и точные ответы и делает их более полезными для других.

  • Комментировать .htaccess

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

  • Это не "SEO" -URLs

    Отказ от ответственности: просто домашнее животное разозлится. Вы часто слышите красивые схемы перезаписи URL-адресов, называемые ссылками "SEO" или что-то в этом роде. Хотя это полезно для примеров для Google, это датированное неправильное название.

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

    К каким "красивым" URL-адресам следует концептуально использовать, чтобы сделать веб-сайты удобными для пользователя.

    • Наличие понятных и понятных схем ресурсов.
    • Обеспечение долговременности URL-адресов (AKA permalinks).
    • Обеспечение возможности обнаружения через /common/tree/nesting.

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

Инструменты

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

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

Ответ 4

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

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

  • Используйте PATH_INFO, Luke

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

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

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

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

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

    Простейшая опция также избегать .php "расширений файлов" в URL-адресах:

    Options +MultiViews
    

    Это означает, что Apache выбирает article.php для HTTP-запросов на /article из-за соответствующего базового имени. И это хорошо работает вместе с вышеупомянутой функцией PATH_INFO. Таким образом, вы можете просто использовать URL-адреса, такие как http://example.com/article/virtual/title. Это имеет смысл, если у вас есть традиционное веб-приложение с несколькими точками/сценариями PHP-скриптов.

    Обратите внимание, что MultiViews имеет другую/более широкую цель. Это требует очень незначительного штрафа за производительность, потому что Apache всегда ищет другие файлы с соответствующими базовыми именами. Это фактически означало Content-Negotiation, поэтому браузеры получают лучшую альтернативу среди доступных ресурсов (например, article.en.php, article.fr.php, article.jp.mp4).

  • SetType или SetHandler для сценариев без расширения .php

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

    DefaultType application/x-httpd-php
    

    Таким образом, вы можете просто переименовать article.php script только article (без расширения), но все же обработать его как PHP скрипт.

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

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

    Это несколько зависит от настройки вашего сервера и используемого PHP SAPI. Общие альтернативы включают ForceType application/x-httpd-php или AddHandler php5-script.

    Снова обратите внимание, что такие настройки распространяются от одного .htaccess до подпапок. Вы всегда должны отключать выполнение script (SetHandler None и Options -Exec или php_flag engine off и т.д.) Для статических ресурсов, а также загружать/каталоги и т.д.

  • Другие схемы перезаписи Apache

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

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

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

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

    Подробнее см. http://httpd.apache.org/docs/2.2/urlmapping.html.