"Довольно ссылки" - это часто запрашиваемая тема, но она редко объясняется полностью. mod_rewrite - это один из способов создания "хороших ссылок", но он сложный и его синтаксис очень краткий, трудно поддающийся обработке, и документация предполагает определенный уровень владения HTTP. Может ли кто-нибудь объяснить простым языком, как работают "красивые ссылки" и как mod_rewrite может быть использован для их создания?
Другие распространенные имена, псевдонимы, термины для чистых URL-адресов: URL-адреса RESTful, дружественные URL-адреса, URL-адреса, ориентированные на SEO, Slugging, URL-адреса MVC (возможно, неправильное обозначение)
Чтобы понять, что вам нужно знать о 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 не волшебным образом делает все ваши URL "хорошенькими". Это распространенное недоразумение. Если у вас есть эта ссылка на вашем веб-сайте:
<a href="/my/ugly/link.php?is=not&very=pretty">
там ничего не может сделать mod_rewrite, чтобы сделать это красивым. Чтобы сделать это красивой ссылкой, вы должны:
Измените ссылку на красивую ссылку:
<a href="/my/pretty/link">
Используйте mod_rewrite на сервере для обработки запроса URL /my/pretty/link
используя любой из описанных выше способов.
(Можно использовать mod_substitute
для преобразования исходящих HTML-страниц и их содержащихся ссылок. Хотя это обычно больше усилий, чем просто обновление ваших HTML-ресурсов.)
Там может быть много mod_rewrite и очень сложные правила соответствия, которые вы можете создать, включая цепочку нескольких переписываний, проксирование запросов на совершенно другую службу или машину, возврат определенных кодов статуса HTTP в виде ответов, перенаправление запросов и т.д. Это очень мощный и может быть использован для очень хорошо, если вы понимаете основной механизм запроса HTTP-ответа. Это не делает ваши ссылки хорошими.
См. Официальную документацию для всех возможных флагов и опций.
Чтобы расширить отменить ответ, я хотел бы привести несколько примеров и объяснений некоторых других функций 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
в правой части правила-
или +
(примечание +
экранировано с обратная косая черта, так как без нее это будет выполняться как символ повторения регулярных выражений). На это можно ссылаться на $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
проверит, существует ли файл на текущем сервере и выполняется только переписывание, если оно неДля есть много других полезных ресурсов:
^/
для использования .htaccess
.)И новые регулярные выражения, доступные для новичков:
.*
соответствует любой, даже пустой строке. Вы не хотите использовать этот шаблон повсюду, но часто в последнем правиле резервного копирования.[^/]+
чаще используется для сегментов пути. Он соответствует любому, кроме косой черты.\d+
соответствует только числовым строкам.\w+
соответствует буквенно-цифровым символам. Он в основном сокращен для [A-Za-z0-9_]
.[\w\-]+
для сегментов пути "slug", используя буквы, цифры, тире -
и _
[\w\-.,]+
добавляет периоды и запятые. Предпочитаете экранированную \-
черту в […]
charclasses.\.
обозначает буквальный период. В противном случае .
вне […]
является заполнителем для любого символа.Каждый из этих заполнителей обычно заключен в скобки (…)
в качестве группы захвата. И весь шаблон часто в ^………$
начинает + маркеры конца. Цитирование "шаблонов" необязательно.
Следующие примеры являются 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
# └───────────────────────────┘
/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
, если это становится слишком сложным.
/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
или просто псевдонимов имен каталогов только для отдельных (реальных/реальных) файлов.
/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 они интерпретируются буквальными символами для всех относительных сегментов пути.
Частые дубликаты:
RewriteCond %{REQUEST_URI} !-f
RewriteCond %{REQUEST_URI} !-d
RewriteRule ^.*$ index.php [L]
который часто используется фреймворками PHP или скриптами WebCMS/портала. Фактическое разделение пути затем обрабатывается на PHP с помощью $_SERVER["REQUEST_URI"]
. Таким образом, концептуально это в значительной степени противоположно обработке URL-адресов "per mod_rewrite". (Просто используйте FallBackResource
вместо.)
www.
из имени хостаОбратите внимание, что это не копирует строку запроса и т.д.
# ┌──────────┐
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] │
RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │
# ↓ └───┼────────────┘
# └───────────────┘
См. также:
· переписывание URL-адресов для разных протоколов в .htaccess
· Общий htaccess перенаправляет WWW на не-www
· .htaccess - как заставить "www." в общем виде?
Обратите внимание, что комбинации RewriteCond/RewriteRule могут быть более сложными, причем совпадения (%1
и $1
) взаимодействуют в обоих направлениях:
Руководство Apache - введение в mod_rewrite, Copyright 2015 Apache Software Foundation, AL-2.0
HTTPS://
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://example.com/$1 [R,L]
Смотрите также: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+)$ $1.php [L] # or [END]
Смотрите также: Удаление расширения .php с помощью mod_rewrite
Смотрите: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility
Смотрите mod_rewrite, php и файл .htaccess
.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
.
500
. То, что он обычно выполняет, это украсить ваших пользователей с ошибками HTTP 404
. (Не намного удобнее, если вы думаете об этом.)То, что кажется заманчивым, как обобщенная защита, часто оказывается препятствием на практике.
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
Общим неправильным вмешательством является то, что RewriteCond блокирует несколько RewriteRules (потому что они визуально расположены вместе):
RewriteCond %{SERVER_NAME} localhost
RewriteRule ^secret admin/tools.php
RewriteRule ^hidden sqladmin.cgi
Что это не по умолчанию. Вы можете связать их, используя флаг [S=2]
. Иначе вам придется повторять их. Иногда вы можете создать "перевернутое" первичное правило для [END] перезаписи обработки раньше.
Вы не можете сопоставить 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 включив предварительные исследования и попытки (избегайте избыточных ответов), продемонстрируйте основные regex и:
$_SERVER
, если это связано с несоответствием параметра.access.log
и error.log
, чтобы проверить, к чему решили существующие правила. Еще лучше, a rewrite.log
summary.Это устанавливает более быстрые и точные ответы и делает их более полезными для других.
.htaccess
Если вы где-то копируете примеры, позаботьтесь о включении # comment and origin link
. Хотя это просто плохие манеры, чтобы опустить атрибуцию,
это часто действительно повредит обслуживание позже. Документируйте любой код или источник учебника. В частности, в то время как вы не должны
тем более заинтересованы в том, чтобы не рассматривать их как волшебные черные ящики.
Отказ от ответственности: просто домашнее животное разозлится. Вы часто слышите красивые схемы перезаписи URL-адресов, называемые ссылками "SEO" или что-то в этом роде. Хотя это полезно для примеров для Google, это датированное неправильное название.
Ни одна из современных поисковых систем действительно не нарушена .html
и .php
в сегментах маршрута, или ?id=123
строки запроса, если на то пошло. Поисковые системы старых, такие как AltaVista, избегали обхода сайтов с потенциально затруднительными путями доступа. Современные сканеры часто даже жаждут глубоких веб-ресурсов.
К каким "красивым" URL-адресам следует концептуально использовать, чтобы сделать веб-сайты удобными для пользователя.
/common/tree/nesting
.Однако не жертвуйте уникальными требованиями к конформизму.
Существуют различные онлайн-инструменты для генерации RewriteRules для большинства URL-адресов с параметрами GET:
В основном просто выведите [^/]+
общие заполнители, но, вероятно, достаточно для тривиальных сайтов.
Многие основные схемы виртуальных URL-адресов могут быть достигнуты без использования RewriteRules. Apache позволяет запускать скрипты PHP без расширения .php
и с виртуальным аргументом PATH_INFO
.
В настоящее время AcceptPathInfo On
часто включается по умолчанию. Что в принципе позволяет .php
и другим URL-адресам ресурсов переносить виртуальный аргумент:
http://example.com/script.php/virtual/path
Теперь этот /virtual/path
отображается в PHP как $_SERVER["PATH_INFO"]
, где вы можете обрабатывать любые дополнительные аргументы, как вам нравится.
Это не так удобно, как разделить сегменты входного пути Apache на $1
, $2
, $3
и передать их как различные переменные $_GET
в PHP. Это просто эмуляция "симпатичных URL-адресов" с меньшим усилием настройки.
.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
).
.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 предоставляет функции 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.
reference-mod-rewrite-url-rewriting-explained
является/questions/20563772/reference-mod-rewrite-url-rewriting-explained
,/questions/20563772/reference-mod-rewrite-url-rewriting-explained
является симпатичным URL.