Мне удалось создать .htaccess
файл, который делает то, что я хотел (см. пояснения и вопросы после блока кода):
<IfModule mod_rewrite.c>
RewriteEngine On
#1 If the requested file is not url-mapper.php (to avoid .htaccess loop)
RewriteCond %{REQUEST_FILENAME} (?<!url-mapper\.php)$
#2 If the requested URI does not end with an extension OR if the URI ends with .php*
RewriteCond %{REQUEST_URI} !\.(.*) [OR]
RewriteCond %{REQUEST_URI} \.php.*$ [NC]
#3 If the requested URI is not in an excluded location
RewriteCond %{REQUEST_URI} !^/seo-urls\/(excluded1|excluded2)(/.*)?$
#Then serve the URI via the mapper
RewriteRule .* /seo-urls/url-mapper.php?uri=%{REQUEST_URI} [L,QSA]
</IfModule>
Это то, что .htaccess
стоит сделать:
Правило # 1 проверяет, что запрошенный файл не url-mapper.php
(чтобы избежать бесконечных циклов перенаправления). Этот файл всегда будет в корне домена.
Правило # 2 то .htaccess
должны перехватывать только те URL-адреса, которые не заканчиваются расширением (www.example.com
-> поймать | www.example.com/catch-me
-> поймать | www.example.com/dont-catch.me
-> не лови) и URL-адреса, заканчивающиеся на .php\*
файлы (.php
, .php4
, .php5
, .php123
, ...).
Правило # 3 некоторые каталоги (и дочерние) могут быть исключены из .htaccess
(в таком случае /seo-urls/excluded1
и /seo-urls/excluded2
).
Наконец .htaccess
кормит картографа скрытый Параметр GET с именем uri
содержащий запрошенный URI.
Даже если я протестировал и все работает, я хочу знать, правильно ли то, что я делаю (и является ли это «лучшим» способом сделать это). Я многому научился с этим "проектом", но все еще считаю себя новичком в .htaccess
и регулярные выражения, поэтому я хочу трижды проверить его перед запуском в производство ...
<IfModule mod_rewrite.c>
Нет необходимости в <IfModule>
обертка. См. Этот вопрос в стеке веб-мастеров: https://webmasters.stackexchange.com/questions/112600/is-checking-for-mod-write-really-needed
#1 If the requested file is not url-mapper.php (to avoid .htaccess loop) RewriteCond %{REQUEST_FILENAME} (?<!url-mapper\.php)$
Нет необходимости в отрицательном просмотре назад ((?<!
). Было бы предпочтительнее просто иметь отрицательное регулярное выражение. т.е. с !
префикс. Например: !^/url-mapper\.php$
(т.е. не "/url-mapper.php") И проверьте REQUEST_URI
(полный URL-путь) вместо REQUEST_FILENAME
(абсолютный путь к файловой системе).
Однако вы заявляете, что «этот файл всегда будет в корне домена», но вы переписываете на /seo-urls/url-mapper.php
- который не находится в корне домена.
Итак, это должно быть написано строго так:
RewriteCond %{REQUEST_URI} !^/seo-urls/url-mapper\.php$
Если вы не уверены, есть только один url-mapper.php
файл в вашей системе, то регулярное выражение может быть уменьшено до !/url-mapper\.php$
.
Или проверьте это в RewriteRule
шаблон вместо этого (поскольку вы больше ничего не делаете с RewriteRule
шаблон В настоящее время). Это было бы незначительно более эффективным.
#2 If the requested URI does not end with an extension OR if the URI ends with .php* RewriteCond %{REQUEST_URI} !\.(.*) [OR] RewriteCond %{REQUEST_URI} \.php.*$ [NC]
Первое условие слишком общее - оно захватывает любой URL-адрес, который просто содержит точку в любом месте URL-пути, а не просто расширение файла. Также нет необходимости в подшаблонах захвата. Предполагая, что расширение файла составляет от 2 до 4 символов, тогда шаблон вроде !\.\w{2,4}$
точнее ловил бы только расширения файлов.
Как правило, регулярное выражение должно быть максимально ограничительным. Второе условие, что Спички .php
расширение (я) можно сделать более ограничительным, в настоящее время оно соответствует .php
везде в URL-пути. Например: \.php\d{0,4}$
- хотя на самом деле у вас, вероятно, есть не более 1 цифры после .php
, так \.php\d?$
наверное было бы лучше.
Я также сомневаюсь, что NC
(без учета регистра) здесь действительно требуется флаг. Вам действительно нужно соответствовать .PHP
или .PhP
?
#3 If the requested URI is not in an excluded location RewriteCond %{REQUEST_URI} !^/seo-urls\/(excluded1|excluded2)(/.*)?$
Хорошо, но конечный (/.*)?$
лишнее. Вы сопоставляете все, что просто начинается /seo-urls/excluded1
и т.д. Кроме того, нет необходимости использовать обратную косую черту с escape-чертой (любопытно, что вы избежали средней косой черты, но не двух других).
#Then serve the URI via the mapper RewriteRule .* /seo-urls/url-mapper.php?uri=%{REQUEST_URI} [L,QSA]
Хорошо, хотя регулярное выражение .*
(который соответствует всему) можно упростить до ^
- что хорошо для всего, но на самом деле ничего не соответствует - это все, что вам нужно.
Однако я упомянул вверху о выполнении url-mapper.php
проверить в RewriteRule
шаблон вместо. Итак, это будет написано:
RewriteRule !^seo-urls/url-mapper\.php$ /seo-urls/url-mapper.php?uri=%{REQUEST_URI} [L,QSA]
В RewriteRule
шаблон обрабатывается первым, перед любым из условий, поэтому всегда предпочтительно делать то, что вы можете в RewriteRule
директиву, а не разделять ее на другое условие.
Объединив вышеупомянутые моменты, мы имеем:
RewriteEgnine On
#2 If the requested URI does not end with an extension OR if the URI ends with .php*
RewriteCond %{REQUEST_URI} !\.\w{2,4}$ [OR]
RewriteCond %{REQUEST_URI} \.php\d?$
#3 If the requested URI is not in an excluded location
RewriteCond %{REQUEST_URI} !^/seo-urls\/(excluded1|excluded2)
#Then serve the URI via the mapper
RewriteRule !^seo-urls/url-mapper\.php$ /seo-urls/url-mapper.php?uri=%{REQUEST_URI} [QSA,L]
2a разрешит /some.dir/file из-за "." посередине. Вы можете захотеть что-то вроде !\..[1,3]
, это будет зависеть от структуры вашего файла / каталога.
В RewriteRules нормально использовать сопоставление подстановки вместо имен переменных:
RewriteRule (.*) /seo-urls/url-mapper.php?uri=$5 [L,QSA]
Обратите внимание, что это $5
потому что в настоящее время у вас есть шаблоны соответствия для всех ваших условий.