Как мне заставить флаг [L] RewriteRule (.htaccess) действительно работать? - PullRequest
5 голосов
/ 04 сентября 2010

Для новичков: пытаясь всесторонне описать мою проблему и сформулировать мои вопросы, я произвел огромное количество текста. Если вы не хотите читать все это, мои наблюдения о (читай "доказательство") [L] флаг не работает неправильное представление, из которого все это возникло, находится в Дополнительно наблюдения раздел. Почему я неправильно понял кажущееся поведение, описано в моем ответе, а также решение данной проблемы.

Настройка

<Ч />

В моем файле .htaccess есть следующий код:

# disallow directory indexing
Options -Indexes

# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on

# allow access to robots file
RewriteRule ^robots.txt$ robots.txt [NC,L]

# mangle core request handler address
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]

# mangle web file adresses (move them to application root folder)
# application root folder serves as application GUI address
RewriteRule ^$ web/index.html [L]
# allow access to images
RewriteRule ^(images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]
# allow access to stylesheets
RewriteRule ^(css/.+\.css)$ web/$1 [NC,L]
# allow access to javascript
RewriteRule ^(js/.+\.js)$ web/$1 [NC,L]
# allow access to library scripts, styles and images
RewriteRule ^(lib/js/.+\.js)$ web/$1 [NC,L]
RewriteRule ^(lib/css/.+\.css)$ web/$1 [NC,L]
RewriteRule ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]

# redirect all other requests to application address
# RewriteRule ^(.*)$ /foo/ [R]

Мое веб-приложение (и его файл .htaccess) находится в подпапке foo в DOCUMENT_ROOT (доступ из браузера осуществляется как http://localhost/foo/). Он содержит основную часть PHP, расположенную в foo/core, и часть JavaScript, которая находится в foo/web. Как видно из приведенного выше кода, я хочу разрешить доступ только к одноядерному сценарию, который обрабатывает все запросы из графического интерфейса пользователя и «защищает» веб-файлы и перенаправляет все остальные запросы на базовый адрес приложения (последняя прокомментированная директива).


Задача

<Ч />

Поведение

Это работает, пока я не попробую последнюю часть, раскомментировав последнюю директиву перенаправления. Если я прокомментирую еще несколько строк, соответствующие части страницы перестанут работать и т. Д.

Однако, когда я раскомментирую последнюю строку, которая должна выполняться только в случае сбоя сопоставления всех предыдущих правил (по крайней мере, это я так понимаю), страница переходит в цикл перенаправления (Firefox выдает страницу ошибки с чем-то вроде «Эта страница не является» t перенаправить правильно "), потому что он перенаправляет на http://localhost/foo/ снова и снова и снова, навсегда.

Вопросы

Что я не понимаю, так это обработка этого правила:

RewriteRule ^$ web/index.html [L]

в частности флаг [L]. Флаг, очевидно, не работает для меня. Когда последняя строка комментируется, она правильно перенаправляет, но когда я раскомментирую ее, она всегда обрабатывается, даже если перезапись должна останавливаться на флаге [L]. У кого-нибудь есть идеи?

Кроме того, на sidenote, я был бы рад узнать, почему моя следующая попытка исправить это тоже не работает:

RewriteEngine on
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]
RewriteRule ^(.*)$ web/$1 [L]
RewriteRule ^.*$ /foo/ [L]

На самом деле это вообще не работает. Даже если я удаляю последнюю строку, она все равно ничего не перенаправляет правильно. Как работает перенаправление в первом примере, если оно не работает во втором?

Мне также было бы очень полезно, если бы кто-нибудь знал, как на самом деле отладить эти директивы. Я трачу на это часы, даже не зная, что может быть не так.


Дополнительные наблюдения

<Ч />

После того, как попробовал совет, данный bbadour (не то, чтобы я не пробовал его раньше, но теперь, когда у меня было второе мнение, я дал ему еще один шанс), и он не сработал, я придумал Следующее наблюдение. Переписав последнюю строку к этому:

RewriteRule ^(.*)$ /foo/?uri=$1 [R,L]

или это

RewriteRule ^(.*)$ /foo/?uri=%{REQUEST_URI} [R,L]

и используя панель Net Firebug, я обнаружил больше доказательств того, что флаг [L] явно не работает, как ожидалось в ранее упомянутом правиле RewriteRule ^$ web/index.html [L] (давайте теперь будем называть его ПРАВИЛОМ). В первом случае я получаю [...]uri=web/index.html, во втором [...]uri=/foo/web/index.html. Это означает, что выполняется ПРАВИЛО (переписывает ^ $ в web / index.html), но переписывание на этом не заканчивается. Есть еще идеи, пожалуйста?

Ответы [ 3 ]

12 голосов
/ 04 сентября 2010

После нескольких часов поиска и тестирования я наконец нашел реальную проблему и решение. Надеюсь, это поможет кому-то еще, когда они столкнутся с той же проблемой.

Причина наблюдаемого поведения

<Ч />

.htaccess файл обрабатывается после каждого перенаправления (даже без флага [R]) ,

, что означает, что после обработки RewriteRule ^$ web/index.html [L] mod_rewrite корректно останавливает перезапись, переходит в конец файла, правильно перенаправляет на /foo/web/index.html, , а затем сервер начинает обработку файла .htaccess для нового местоположения. , который является тем же файлом . Теперь только последнее правило перезаписи соответствует и перенаправляет обратно на /foo/ (на этот раз с [R], поэтому перенаправление можно наблюдать в браузере) ... и файл .htaccess обрабатывается снова, и снова, и снова .. .

Еще раз для ясности: поскольку можно наблюдать только жесткие перенаправления, похоже, что флаг [L] игнорируется, но это не так. Вместо этого .htaccess обрабатывается два раза, перенаправляя туда и обратно между /foo/ и /foo/web/index.html.


Решение

<Ч />

Запретить прямой доступ к подпапке

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

RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]

Для соответствия этому правилу перезаписи должны выполняться два условия. Во-первых, во второй строке «локальный URI» должен начинаться с web/ (что соответствует абсолютному веб-URI /foo/web/). Во-вторых, в первой строке реальный URI запроса также должен начинаться с /foo/web/. В совокупности это означает, что правило совпадает только тогда, когда файл внутри подпапки web/ запрашивается непосредственно из браузера, и в этом случае мы хотим выполнить жесткое перенаправление.

Перенаправление на разрешенный контент из корневой папки в подпапку (soft)

RewriteCond $1 !^web/
RewriteCond $1 ^(.+\.(html|css|js|ico|png|bmp|jpg|gif))?$
RewriteRule ^(.*)$ web/$1 [L,NC]

Мы хотим перенаправить на разрешенный контент, только если мы еще этого не сделали, отсюда и первое условие. Второе условие определяет маску для разрешенного содержимого. Все, что соответствует этой маске, будет мягко перенаправлено, возможно, возвращая ошибку 404, если контент не существует.

Скрыть все содержимое, не находящееся в подпапке или не разрешенное

RewriteRule !^web/ /foo/ [L,R]

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

* *
тысяча сорок-девять

Реальный пример

<Ч />

Мой код, показанный в моем «вопросе» после использования упомянутых выше советов по решению, постепенно трансформируется в следующее:

# disallow directory indexing
Options -Indexes

# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on

# allow access to robots file
RewriteRule ^robots.txt$ - [NC,L]

# mangle core request handler address
# disallow direct access to core request handler
RewriteCond %{THE_REQUEST} !^(GET|POST)\ /asm/core/handleCoreRequest.php
RewriteRule ^core/handleCoreRequest.php$ - [L]
# allow access to request handler under alias
RewriteRule ^core/$ core/handleCoreRequest.php [NC,QSA,L]

# mangle GUI files adressing (move to application root folder)
# disallow direct access to GUI subfolder
RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]
# allow access only to correct filetypes in appropriate locations
RewriteCond $1 ^$ [OR]
RewriteCond $1 ^(images/.+\.(ico|png|bmp|jpg|gif))$ [OR]
RewriteCond $1 ^(css/.+\.css)$ [OR]
RewriteCond $1 ^(js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/css/.+\.css)$ [OR]
RewriteCond $1 ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$
RewriteRule ^(.*)$ web/$1 [L,NC]

# hide all files not in GUI subfolder that are not whitelisted above
RewriteRule !^web/ /foo/ [L,R]


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

0 голосов
/ 04 сентября 2010

Попробуйте использовать:

RewriteRule ^(.*)$ /foo/ [R,L]

Если он все еще зацикливается, поместите перед ним RewriteCond, чтобы пропустить правило, если оно уже /foo/

0 голосов
/ 04 сентября 2010

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

...