Как использовать apache2 mod_rewrite в директиве Directory, которая использует подстановочные знаки? - PullRequest
11 голосов
/ 15 июня 2011

Я написал веб-приложение, которое запускаю на выделенном сервере для размещения веб-приложения. Экземпляры этого веб-приложения доступны в разных доменах, и у каждого домена есть своя собственная копия файлов веб-приложения, позволяющая при необходимости выполнить настройку.

Я использую Apache / 2.2.16 под Debian Squeeze.

Я делаю все настройки в соответствии с директивой VirtualHost и не использую файлы .htaccess.

Чтобы упростить настройку apache, я хочу сохранить одну директиву Directory, такую ​​как:

<Directory "/srv/www/*/public/">
  RewriteEngine on
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !=/favicon.ico
  RewriteCond %{REQUEST_URI} !=/robots.txt
  RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA]
</Directory>

Однако RewriteRule дает неправильные результаты, потому что при использовании подстановочного значения Directory, он не может удалить префикс для каждого каталога. Вот вывод журнала перезаписи:

[rid#b9832078/initial] (3) [perdir /srv/www/*/public/] applying pattern '^(.+)$' to uri '/srv/www/domain1/public/login'
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-f' => matched
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-d' => matched
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/login' pattern='!=/favicon.ico' => matched
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/login' pattern='!=/robots.txt' => matched
[rid#b9832078/initial] (2) [perdir /srv/www/*/public/] rewrite '/srv/www/domain1/public/login' -> '/index.php?q=/srv/www/domain1/public/login'
[rid#b9832078/initial] (3) split uri=/index.php?q=/srv/www/domain1/public/login -> uri=/index.php, args=q=/srv/www/domain1/public/login
[rid#b9832078/initial] (1) [perdir /srv/www/*/public/] internal redirect with /index.php [INTERNAL REDIRECT]
[rid#b9847440/initial/redir#1] (3) [perdir /srv/www/*/public/] applying pattern '^(.+)$' to uri '/srv/www/domain1/public/index.php'
[rid#b9847440/initial/redir#1] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/index.php' pattern='!-f' => not-matched
[rid#b9847440/initial/redir#1] (1) [perdir /srv/www/*/public/] pass through /srv/www/domain1/public/index.php

Проблема в том, что RewriteRule 'uri' является путем к файловой системе, а не путем URL, что приводит к неправильной строке запроса: q = / srv / www / domain1 / public / login

Явно указав путь к каталогу, например:

<Directory "/srv/www/domain1/public/">
  RewriteEngine on
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !=/favicon.ico
  RewriteCond %{REQUEST_URI} !=/robots.txt
  RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA]
</Directory>

Работает просто отлично, и вот выходные данные журнала перезаписи, показывающие правильное поведение (разница в том, что новая первая дополнительная строка обеспечивает правильный ввод для остальной части перезаписи, что приводит к правильной строке запроса: q = Логин ):

[rid#b9868048/initial] (3) [perdir /srv/www/domain1/public/] strip per-dir prefix: /srv/www/domain1/public/login -> login
[rid#b9868048/initial] (3) [perdir /srv/www/domain1/public/] applying pattern '^(.+)$' to uri 'login'
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-f' => matched
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-d' => matched
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/login' pattern='!=/favicon.ico' => matched
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/login' pattern='!=/robots.txt' => matched
[rid#b9868048/initial] (2) [perdir /srv/www/domain1/public/] rewrite 'login' -> '/index.php?q=login'
[rid#b9868048/initial] (3) split uri=/index.php?q=login -> uri=/index.php, args=q=login
[rid#b9868048/initial] (1) [perdir /srv/www/domain1/public/] internal redirect with /index.php [INTERNAL REDIRECT]
[rid#b987d5f8/initial/redir#1] (3) [perdir /srv/www/domain1/public/] strip per-dir prefix: /srv/www/domain1/public/index.php -> index.php
[rid#b987d5f8/initial/redir#1] (3) [perdir /srv/www/domain1/public/] applying pattern '^(.+)$' to uri 'index.php'
[rid#b987d5f8/initial/redir#1] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/index.php' pattern='!-f' => not-matched
[rid#b987d5f8/initial/redir#1] (1) [perdir /srv/www/domain1/public/] pass through /srv/www/domain1/public/index.php

Я ожидаю, что столкнулся с ошибкой в ​​Apache, но если это не так, что я делаю не так?

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

Так есть ли что-то, что должно измениться на RewriteCond / Rules при использовании в каталоге с подстановочными знаками?

Примечание для любопытных: для дальнейшего упрощения я использую один VirtualHost с использованием VirtualDocumentRoot - однако это не связано, поскольку эта проблема реплицируется с использованием DocumentRoot и тестированием в одном домене.

EDIT

Хорошо, я пересмотрел это, основываясь на ответе regilero , и вот что происходит - перемещение перезаписанного, как есть, из Справочника приводит к небольшой начальной проблеме изменения строки запроса от "login" до "/ login", это исправляется изменением RewriteRule на: RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA], исправляющее мой предыдущий комментарий "необъяснимо не удается".

После этого все статические файлы не загружаются, вот журнал перезаписи, показывающий эту проблему:

[rid#b7bc7fa0/initial] (2) init rewrite engine with requested uri /login
[rid#b7bc7fa0/initial] (3) applying pattern '^/(.+)$' to uri '/login'
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!-f' => matched
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!-d' => matched
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!=/favicon.ico' => matched
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!=/robots.txt' => matched
[rid#b7bc7fa0/initial] (2) rewrite '/login' -> '/index.php?q=login'
[rid#b7bc7fa0/initial] (3) split uri=/index.php?q=login -> uri=/index.php, args=q=login
[rid#b7bc7fa0/initial] (2) local path result: /index.php
[rid#b7bc7fa0/initial] (2) prefixed with document_root to /srv/www/domain1/public/index.php
[rid#b7bc7fa0/initial] (1) go-ahead with /srv/www/domain1/public/index.php [OK]
[rid#b7be6b80/initial] (2) init rewrite engine with requested uri /static/css/common.css
[rid#b7be6b80/initial] (3) applying pattern '^/(.+)$' to uri '/static/css/common.css'
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!-f' => matched
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!-d' => matched
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!=/favicon.ico' => matched
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!=/robots.txt' => matched
[rid#b7be6b80/initial] (2) rewrite '/static/css/common.css' -> '/index.php?q=static/css/common.css'
[rid#b7be6b80/initial] (3) split uri=/index.php?q=static/css/common.css -> uri=/index.php, args=q=static/css/common.css
[rid#b7be6b80/initial] (2) local path result: /index.php
[rid#b7be6b80/initial] (2) prefixed with document_root to /srv/www/domain1/public/index.php
[rid#b7be6b80/initial] (1) go-ahead with /srv/www/domain1/public/index.php [OK]

Но, как я уже сказал в своем комментарии к ответу regilero , это решается префиксом директив RewriteCond TestString с% {DOCUMENT_ROOT}. Однако использование% {DOCUMENT_ROOT} не работает при использовании VirtualDocumentRoot.

Мне кажется неправильным, что префикс% {DOCUMENT_ROOT} необходим.

EDIT

REQUEST_FILENAME

Полный путь локальной файловой системы к файлу или сценарию, соответствующий запрос, если это уже было определено сервером на момент Ссылка на REQUEST_FILENAME. В противном случае, например, при использовании в контекст виртуального хоста, то же значение, что и REQUEST_URI.

, которая объясняет необходимость использования префикса DOCUMENT_ROOT.

Я обновил правила переписывания так:

RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteCond %{REQUEST_URI} !^/static/
RewriteRule ^/(.+)$ /index.php?q=$1 [PT,L,QSA]

Что работает нормально (Примечание: флаг PT необходим, чтобы избежать преждевременного перевода пути URL-адреса в путь файловой системы при использовании VirutalDocumentRoot). Основное изменение в поведении здесь заключается в том, что RewriteCond будет необходим для всех точек входа в приложение - аналогично строке / static.

EDIT

Вот мое последнее воплощение директив Rewrite в VirtualHost вне директив директории:

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/static/
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteRule ^/(.+)$ /index.php?q=$1 [NS,PT,L,QSA]
RewriteRule ^/$ /index.php [NS,PT,L,QSA]

Я добавил флаг NS, чтобы избежать дополнительной внутренней оценки, и добавил вторую директиву RewriteRule в пользу использования mod_dir и DirectoryIndex. Мое приложение ожидает, что для корневого URL не задан параметр q =, в противном случае будет достаточно одного RewriteRule из RewriteRule ^/(.*)$ /index.php?q=$1 [NS,PT,L,QSA], если приложение будет обновлено, чтобы принять пустой параметр q= для корневого URL. Я могу сделать это в будущем.

1 Ответ

8 голосов
/ 15 июня 2011

Очень хороший и детальный вопрос.

Вы наверняка столкнулись с ошибкой или хотя бы недокументированным доменом rewriteRule.Документация утверждает, что:

  • Механизм перезаписи может использоваться в файлах .htaccess и в разделах с некоторой дополнительной сложностью.
  • Чтобы включить механизм перезаписи в этом контексте,вам нужно установить «RewriteEngine On» и «Options FollowSymLinks» должны быть включены.Если ваш администратор отключил переопределение FollowSymLinks для каталога пользователя, вы не можете использовать механизм перезаписи.Это ограничение требуется по соображениям безопасности.
  • При использовании механизма перезаписи в файлах .htaccess префикс для каждого каталога (который всегда одинаков для определенного каталога) автоматически удаляется для сопоставления с шаблоном RewriteRule и автоматически добавляетсяпосле любой относительной (не начинающейся с косой черты или имени протокола) замены встречается конец набора правил.См. Директиву RewriteBase для получения дополнительной информации о том, какой префикс будет добавлен обратно в относительные подстановки.

Так что никакое упоминание о том, что инструкция <Directory> с подстановочными знаками не сможет убратьпрефикс для каждого каталога.И игра с RewriteBase не поможет вам, это сделано для того, чтобы перестроить окончательный Url, чтобы не изменить работу perdir.

Но, как вы можете видеть в начале, есть " с некоторой дополнительной сложностью "предложение. Манипуляции с каталогами, выполняемые mod-rewrite, медленнее и сложнее, чем обычные вне каталога RewriteRules .Об этом также говорится в документации , в основном из-за манипуляций с полосой perdir.А это значит, что вы также можете написать свой rewriteRule из раздела <Directory> в вашем VirtualHost.

  • это будет быстрее
  • эта ошибка не будет затронута
  • это может иметь некоторые побочные эффекты, если некоторые несуществующие файлы не должны отображатьсяк вашему правилу index.php?q=$1 в некоторых других каталогах.Но я совершенно уверен, что это не проблема в вашем случае.

Так что просто напишите (без подстановочного каталога):

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA]

И это должно работать, позвольте мнеизвестно, если это приводит к новым проблемам.

Редактировать:

Хорошо, забудьте о том, что REQUEST_FILENAME еще не полностью определен в контексте VirtualHost, он задокументирован, это ' normal ', когда условие применяется, поиск файла по реальному пути еще не завершен, поэтому вы должны добавить корень документа.Таким образом, на самом деле ваше окончательное решение должно быть таким:

RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA]

Я попробовал второе, избегая DOCUMENT_ROOT, используя позднюю оценку REQUEST_FILENAME (% {LA-U: REQUEST_FILENAME} содержитокончательный путь, который на самом деле является полным путем к index.php в случае несуществующих файлов), но единственный способ заставить его работать - это добавить второе правило и условие Or во второе, менее простое, поэтому первоерешение конечно лучше (ПОЦЕЛУЙ).

  RewriteCond %{LA-U:REQUEST_FILENAME} !-f [OR]
  RewriteCond %{LA-U:REQUEST_FILENAME} !/index.php
  RewriteCond %{LA-U:REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !=/favicon.ico
  RewriteCond %{REQUEST_URI} !=/robots.txt
  RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA]

  RewriteCond %{LA-U:REQUEST_FILENAME} /index.php
  RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA]
...