20
2011
Apache MultiViews, или почему web-сервер находит нужный файл даже без указания расширения
Столкнулся я с этой странностью совершенно случайно. Один мой коллега по ошибке забыл добавить расширение к подключаемому JavaScript-файлу. В коде страницы выглядело это примерно так:
<script type='text/javascript' src='/js/some'></script>
Каково же было мое удивление, когда Apache вместо вполне ожидаемой 404 страницы и ответа в духе «пошел нафик, такого файла не существует», выдавал корректный JS как ни в чем не бывало. Спрашивается, как же так? Почему Apache при запросе файла без расширения отдаёт тот файл, у которого совпадает имя до расширения? o.O
В ходе экспериментов оказалось, что данная фича работает далеко не на всех web-серверах под управлением Apache — где-то срабатывало, где-то — нет. А ещё было подмечено, что если создать несколько файлов с одним и тем же именем (в нашем случае «some») и различными расширениями (*.js, *css), то сервер всегда отдавал тот файл, который шел первым по алфавиту.
Вообщем делать нечего — принялись гуглить. Сначала безусловно грешили на mod_rewrite. Все же кто как не он по большей части заведует роутингом запросов. Но, увы, поиски в этом направлении ничего не дали. Но зато наткнулись на так называемый mod_negotiation, который входит в стандартную сборку Apache во многих дистрибутивах Linux. С помощью этого модуля из нескольких версий запрашиваемого документа можно выбрать тот, который лучше всего подходит клиенту. Данный мод позволяет серверу выбирать один из вариантов страницы (например язык) в зависимости от запроса пользователя. Имеется несколько параметров, позволяющих определить, какие критерии будут использоваться в процессе согласования. Можно выбирать разные языки, форматы графических файлов и методы сжатия. А ещё у него имеется одна опция, которая называется MultiViews. Дословно из мануала:
A MultiViews search is enabled by the MultiViews Options. If the server receives a request for /some/dir/foo and /some/dir/foo does not exist, then the server reads the directory looking for all files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client’s requirements, and returns that document.
Фактически при включении данной опции в настройках виртуального хоста (или же .htaccess) при запросе несуществующего файла без расширения Apache просматривает директорию на наличие файла, соответвующего маске some.* и отдает первый найденный файл. Причем все заголовки (Content-Type, Content-Length) формируются автоматически, согласно реальному расширению файлы. Вот такие дела. Кстати, вместе с заголовками отдается заголовок: Vary: negotiate,accept,Accept-Encoding, который слегка намекает куда надо было «копать» изначально. А вот, собственно, и содержимое виртуального хоста:
<VirtualHost *:80> ... <Directory /srv/www/path/to/project/> Options Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny allow from all </Directory> ... </VirtualHost>
Когда мы убираем параметр MultiViews и перезапускаем демон apache, все становится на свои места, и никаких таинственных документов больше не отдается
Кстати, при изучении мануала по mod_negotiation обратил внимание на пример с отдаваемым документом в зависимости от языковых предпочтений клиента.
Так например, если в настройках Apache задать следующее:
LanguagePriority en fr de
а на сервере существует 3 документа foo.html.en, foo.html.fr и foo.html.de, то по запросу браузера /foo.html будет отдаваться тот документ, язык которого совпадает со значением заголовка Accept-Language браузера.
Не уверен по поводу юзабельности данной фичи, поскольку по гибкости реализация динамической выдачи документа посредством mod_negotiation ну никак не сравнится с аналогичной реализацией различными скриптовыми языками (например PHP, Ruby, Python), которые обычно работают вкупе с веб-сервером. Да и опцию MultiViews скорее всего в большинстве случаев не стоит включать. Конечно же эффект крутой, но ни к чему оно.
Пн | Вт | Ср | Чт | Пт | Сб | Вс |
---|---|---|---|---|---|---|
« Сен | ||||||
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 |
Метки
Рубрики
- Apache (1)
- Highload (4)
- JavaScript (1)
- Linux (3)
- MongoDB (1)
- MySQL (1)
- Perl (1)
- PHP (5)
- Python (5)
- Web-разработка (5)
- Алгоритмы (1)
- За жизнь (2)
- Конференции (6)
С точки зрения безопасности mod_negotiation не желателен.
Возможен частичный листинг файлов.
http://www.wisec.it/sectou.php?id=4698ebdc59d15
Определённо, особенно если хранить скрипты вместе со статическим контентом, то по сути дела можно получить листинг всей структуры сайта. А вот если скрывать все скрипты за пределами DOCUMENT_ROOT, и держать лишь один открытый index.php, то в листинге статического контента нет ничего страшного
Как ни к чему? Например превратить /auto.php а в /auto! Это намного проще чем через Rewrite который может вывести и ошибку 500! Жалко что все забыли про MultiViews!
Пойду обратно на CyberForum.ru напоминать всем о MultiViews!
Весьма спорно… Вместе с этой фичей может появиться куча трудноотлавливаемых багов. Так, например, если рядом с /auto.php положить /auto.jpg , то ни с того ни с сего половина сайта умрет.
Да и вообще у среднестатистического web-приложения в идеале должна быть одна точка входа, а куча разбросанных скриптов не приведет ни к чему хорошему
А если не класть? Всё хорошо!
Почему мой Gravatar не выводится?
Вроде бы перенастроил и всё равно не выводится!
прогрузился
да, но нужно ж все время держать это в уме!