Часто задаваемые вопросы по Apache


Мне (как автору сервера), кажется, что документация достаточно понятна и полна. Однако одни и те же вопросы мне задают регулярно. Наиболее часто задаваемые и составили этот FAQ.
1. На http://apache.lexa.ru/mail-archive/ лежит архив списка рассылки. Могу ли я на него подписаться?
Да. Пошлите письмо из одной строчки subscribe apache-rus по адресу majordomo@lists.lexa.ru.

2. У меня ничего не работает
Корректно ответить на этот вопрос невозможно, но наиболее частые причины такие:
1. Вы скомпилировали Apache без mod_charset или без ключа компиляции -DRUSSIAN_APACHE. Проверить это очень просто - httpd -l выдает список модулей, среди них должен присутствовать mod_charset.c.
Часто это происходит от того, что в качестве заготовки файла Configuration берут Configuration.tmpl, в котором mod_charset.c отсутствует.
2. В вашем файле конфигурации отсутствуют директивы настройки перекодировок. В этом случае сервер работает (во всяком случае должен) в точности как обычный Apache
3. Вы проверяете все каким-то броузером, который корректно показывает любой charset (MS IE 3-4.x, Netscape 4.x, Lynx 2.6+). В этом случае сервер производит все перекодировки, но клиент этого не видит. Единственный способ действительно проверить работу сервера - это использовать telnet www.yourdomain.ru 80 или что-то в этом духе (netcat, netpipes и т.д.).

3. Перекодировка работает, но не для всех URL
Проверьте, описана ли таблица перекодировки для данного сочетания клиентской кодировки и кодировки хранения. Если таблица не описана, то сервер сам построит "пустую" таблицу, т.е. никакой кодировки не будет.

4. Не работает перекодировка в транслитерацию
Проверьте, не используете ли вы случайно директиву CharsetRecodeTable вместо правильной директивы CharsetWideRecodeTable

5. Перекодируются все file uploads
Это - feature.
Варианты решения:
1. запретите все перекодировки для скрипта, который разбирает FileUpload, например таким способом:
2. <Location /path/to/upload.cgi>
3. CharsetDisable On
4. </Location>

и делайте перекодировку сами.
5. Используйте директиву CharsetRecodeMultipartForms, которая появилась в PL23, но при этом вам все-равно придется перекодировать вручную текстовые части запросов. Для этого можно использовать Russian Apache API, доступное в других модулях или Russian Apache Perl API, доступное из mod_perl.

6. Перекодируются PUT-запросы
Это - тоже feature. Решений тоже два:
1. Запретить перекодировку для данного скрипта:
2. Script PUT /path/to/put.cgi
3. <Location /path/to/put.cgi>
4. CharsetDisable On
5. </Location>

6. Используя директиву CharsetRecodeMethodsIn (появилась начиная с PL23) запретить перекодировку для метода PUT.

7. Не перекодируются или перекодируются неправильно запросы к CGI
При обработке CGI и определении кодировки по URL, кодировка определяется по URL CGI-скрипта. Если вы используете определение кодировки по префиксу директории (/win/file.html), а скрипт имеет URL /cgi-bin/myscript.sh, то результат очевиден.

8. Не пeрекодируются proxy-запросы
И не должны. Корректная перекодировка proxy-запросов в общем случае нереальна - далеко не все серверы сообщают о кодировке отдаваемого документа т.е. нельзя узнать из какой кодировки нужно перекодировать в кодировку клиента.
Если вы хотите использовать ProxyPass для отображения соседнего сервера в дерево документов Russian Apache с их перекодировкой (например, для решения проблем перекодировки у MS IIS, Lotus Domino и так далее), то возможны по меньшей мере два варианта:
1. Использование Apache::Gateway (требует mod_perl для работы).
2. Использование самодельного Action-script или подобного CGI, который будет сам делать запросы к удаленному серверу, а затем выдавать их клиенту.

9. При перекодировке "по портам" с одновременным использованием Virtual Hosts, при обращении к виртуальному хосту по порту, отличающемуся от 80 происходит обращение к основному серверу
Сначала прочитайте документацию к оригинальному Apache. Потом найдите у себя в конфигурации строчку
<VirtualHost vhost.my.domain>
и замените ее на
<VirtualHost vhost.my.domain:*>
Продолжайте читать документацию до просветления

10. При использовании NameVirtualHost и <VirtualHost somename:*> все-равно не работает перекодировка "по портам" - сервер ругается "mixing * ports and non-* ports with a NameVirtualHost address is not supported, proceeding with undefined results"
Найдите в конфигурационном файле директиву Port и удалите ее - при Listen на нескольких портах она только мешает.

11. Используется автоматический выбор кодировки. При этом если клиент возвращается в форму кнопкой Back, то получает пустую форму, а при использовании Frames при reload или многих других невинных действиях броузер загружает первоначальный Frameset
Background. При автоматическом определении кодировки, "Russian Apache" ставит заголовок Expires: 1 Jan 1970 для того, чтобы предотвратить оседание документа в транзитных proxy-caches. Если такое оседание будет происходить, то последовательные обращения к документу клиентами с разной кодировкой через один cache будут приводить к выдаче документа в неправильной кодировке. В качестве побочного эффекта, появляется некэшируемость таких документов и в локальных кэшах броузеров и перезагрузка их при нажатии "Back" и так далее.
Новое решение (работает с версии PL28.0)
Начиная с версии 1.3.4PL28.0 у сервера появилась возможность делать автоматическое перенаправление пользователя на документ в "его" кодировке. Это делается директивой CharsetAutoRedirect. Для начала, вам нужно решить, каким образом вы будете формировать URL с жестко-привязанной кодировкой (см обсуждение разных вариантов в этом документе). Допустим, вы решили использовать перекодировку "по портам". Тогда в конфигурации сервера можно написать что-то в таком духе
Listen 80
Listen 8100
Listen 8101
CharsetByPort koi8-r 8100
CharsetByPort windows-1251 8101
CharsetAutoRedirect koi8-r http://www.yourdomain.ru:8100/
CharsetAutoRedirect windows-1251 http://www.yourdomain.ru:8101/windows-1251

Последние две строчки означают, что для всех документов, для которых сервер определил кодировку, будет происходить redirect с добавлением соответствующего префикса к URL. Для настроек по-умолчанию (т.е. как в дистрибутивной конфигурации), при обращении к URL http://www.yourdomain.ru/file.html сервер будет определять кодировку по клиентскому броузеру. Если клиентская кодировка - cp1251, то сервер выдаст redirect на http://www.yourdomain.ru:8101/windows-1251/file.html, если кодировка клиента - кои8, то редирект будет выдан на http://www.yourdomain.ru:8100/file.html
Внимание Есть возможность сконфигурировать сервер так, что будет происходить бесконечный цикл редиректов. Простейший способ этого добиться, это конфигурация вида
CharsetByPort koi8-r 8101 # ! 1.
CharsetByPort windows-1251 8100 # !!!
CharsetSelectionOrder Portnumber Useragent
CharsetAutoRedirect koi8-r http://www.yourdomain.ru:8100/
CharsetAutoRedirect windows-1251 http://www.yourdomain.ru:8101/

После первого редиректа (скажем на www:8101/file.html для клиента с кодировкой cp1251) сервер определит кодировку для нового URL по порту. Поймет что это koi8 (по первой директиве CharsetByPort) и выдаст новый редирект. Процесс будет бесконечным.
Кроме директивы CharsetAutoRedirect, рекомендую использовать еще и CharsetNormalizeToURL, которая предназначена для "нормализации" URL файлов, не требующих перекодировки.
Чтобы выборочно отключить redirect (например, для каких-то определенных броузеров, что может быть нужно для поисковых систем) нужно выставить переменную окружения CHARSET_NOREDIRECT. Например так:
BrowserMatch htdig CHARSET_NOREDIRECT
- это выключит редирект для User-Agent: htdig
Старое решение (версии PL27 и более древние).
1. При приходе клиента на URL с автоматическим определением кодировки, нужно сразу делать Redirect на URL с кодировкой клиента и тем же содержанием. Для "перекодировки по портам" это может выглядеть как-то так:
2. DirectoryIndex index.cgi index.html

index.cgi в корневой директории сервера может выглядеть как-то так:
#!/usr/local/bin/perl
$realindex="/index.html";
%porttable = (
"koi8-r" => 8101,
"windows-1251" => 8102
);
if($ENV{CHARSET_SERVER_NAME}=~/:(8101|8102)/) { #already on another port
print "Location: $ENV{CHARSET_HTTP_METHOD}$ENV{CHARSET_SERVER_NAME}$realindex ";
} else { # WWW host without port - default port assumed
$var = $ENV{CHARSET_SERVER_NAME};
$var=~s/:(d+)$/:$porttable{$ENV{CHARSET}}/;
print "Location: $ENV{CHARSET_HTTP_METHOD}$var$realindex "
}

Для URL с "жестко" заданной кодировкой полезно еще включить директиву CharsetDisableAcceptCharset On - это лечит ошибку в обработке заголовка Vary у MS IE 4.0x. Эта директива доступна, начиная с PL27.0
3. Вместо скрипта можно использовать директивы mod_rewrite, пример можно найти в этом письме в архиве списка рассылки. Необходимое замечание - этим способом плохо обрабатываются директории с паролями.
4. Для форм правильное решение может выглядеть иначе. Нажимать кнопку Back пользователя в 99% случаев вынуждает ленивый webmaster - скрипт обнаруживает, что не все поля формы заполнены и выдает сообщение вида заполнены не все поля формы. Нажмите Back чтобы вернуться и заполнить все поля. Правильное решение - выдать форму со всеми полями тем же скриптом, подсветить незаполненные поля цветом и выдать понятное сообщение об ошибке (Например, Не заполнено поле пароль). Да, это требует больших усилий, но удобство пользователя это окупает.

12. Мой скрипт порождает ссылку на самого себя вместе с параметрами. В параметрах есть русские буквы, которые закодированы как %AA%BB. При обращении по этому URL, на сервер приходят данные, перекодированные дважды.
При выдаче html-документа клиенту происходит только "побуквенная" перекодировка, сам HTML не парсится и элементы вида <a href="script.cgi?param=%AA%BB%CC%DD"> не перекодируются. При этом клиент получает такие URL в кодировке сервера. В то же время, строки GET-запросов с param=%AA%BB%CC%DD перекодируются из кодировки клиента в кодировку сервера. Чтобы все было хорошо, необходимо писать ссылки (HREF) открытым текстом: <a href="script.cgi?param=русский+текст">.
Второй вариант, более соответствующий стандартам, - писать все в виде %AA%BB, но в кодировке клиента. Для этого можно использовать Russian Apache API (в модулях) и Russian Apache Perl API - в модулях, написанных для mod_perl и скриптах, исполняемых под Apache Registry.

13. Не могу собрать вместе Russian Apache и PHP в виде модуля
Возьмите PHP версии 3.0.2a или новее. Там появилась поддержка Russian Apache, которая включается через ./configure --with-mod_charset.

14. Выдача FastCGI не перекодируется
FastCGI использует для вывода не стандартные функции Apache API, а функцию bwrite, вывод которой не перекодируется. Возьмите патч здесь (если используется Russian Apache 1.3.x или новее, то слова USE_TRANSFER_TABLES нужно заменить на RUSSIAN_APACHE).

15. Зачем вообще сервер выдает заголовок Expires ? Это не дает кэшировать документы
Заголовок Expires: выдается сервером именно для того, чтобы избежать кэширования этих документов в "транзитных" proxy-серверах. Допустим на секунду, что такой заголовок не выдается. Допустим, пользователь с Unix (кодировка koi8) пришел на ваш сервер http://yourserver.ru через прокси cache.yourserver.ru. Т.к. документы можно кэшировать, то в прокси осядет этот документ в кодировке koi8. Следующий пользователь того же proxy получит этот документ в koi8, даже если его родная кодировка - cp1251. Придется жать Reload, но скажем поисковые серверы этого делать не будут.
Вывод простой - если один и тот же URL может иметь разное содержимое (кодировки), то он не должен кэшироваться. Кэшироваться могут только документы, кодировка которых определяется по URL, а не по заголовкам, пришедшим от документа.
HTTP/1.1 позволяет более тонко управлять кэшируемостью через заголовок Vary:. Russian Apache поддерживает этот механизм и не выдает Expires на HTTP/1.1 запросы.
Чтобы избежать выдачи некэшируемых документов, необходимо сразу перенаправлять клиента на URL, для которого выдается кэшируемый документ. Как это сделать написано выше.

16. Я использую AutoRedirect. Как сделать так, чтобы поисковые системы не получали его, а получали сам документ ?
Варианты решения:
1. Если вы имеете в виду конкретную поисковую систему, то проще всего использовать настройку вроде такой:
2. BrowserMatch my-cool-spider CHARSET_NOREDIRECT
3. Если хочется, чтобы все неизвестные User-Agents не получали redirect, то возможно вам поможет директива CharsetNoRedirectForDefaultCharset. Эта директива поддерживается начиная с версии сервера 1.3.6 rus/PL28.16.

17. Мне нужно использовать одновременно Russian Apache и SSL, но не получается даже их собрать
Варианты решения:
1. Общий случай. И Russian Apache и mod_ssl требуют внесения изменений в исходные тексты самого Apache (т.е. не могут быть реализованы как независимые модули). К сожалению, часть правок производится в одних и тех же файлах (для Apache 1.3.9 и mod_ssl 2.4.1 это htdocs/index.html, Makefile.tmpl, src/include/httpd.h). Правки вносятся утилитой patch которая работает по контексту. Если контекст нарушен "чужими" правками, то автоматически нужные изменения внесены быть не могут.
Поэтому наиболее общий порядок действий такой - взять Russian Apache нужной версии, взять mod_ssl нужной версии, запустить configure от mod_ssl.
Прежде чем запускать компиляцию, нужно найти все файлы с расширением .rej - в них записываются те изменения, которые не удалось - посмотреть в них и довнести изменения руками.
2. Частные случаи.Russian Apache 1.3.9 PL28.18 сделан совместимым "по патчам" с mod_ssl 2.4.1. Для более поздних (и более ранних) версий возможно потребуется процедура, описанная выше.

18. Есть проблемы при использовании CharsetNormalizeToURL и запароленной картинке

Да, действительно, у нетскейпа есть проблема - он обижается получив редирект-запрос пароля-еще редирект. Решение просто как все гениальное:
AuthType Basic

require valid-user

CharsetNormalizeToURL none

Так как password-protected URLs все-равно не должны кэшироваться в транзитных proxy, то от предлагаемого решения никто не страдает.

19. Когда в Russian Apache появится поддержка [чего-угодно-чего-еще-нет]

(Alex Tutubalin:) Я занимаюсь этим проектом в свободное время и под хорошее настроение. Поэтому никаких сроков пообещать нельзя. Если вам нужна какая-то feature завтра-послезавтра-через-неделю, то вы можете либо написать ее сами (и патч будет принят с благодарностью), либо нанять меня на работу (и все будет сделано в первую очередь).

20. Я нашел ошибку в Russian Apache, как мне добиться ее исправления
Порядок действий:
1. Прочтите документацию к Russian Apache и к оригинальному Apache - может быть это не ошибка, а так и надо ?
2. Внимательно прочитайте список исправлений - возможно у вас не самая новая версия сервера, а ошибка уже исправлена.
3. Убедитесь, что данная ошибка есть в Russian Apache и отсутствует в оригинальном Apache и не относится к 3rd-party модулям. Это можно сделать собрав сервер без mod_charset и проверив ошибку еще раз
4. Задайте вопрос в списке рассылки (см. ответ номер 1).
5. В любом случае убедитесь, что ваша версия Russian Apache поддерживается автором. Поддерживаются версии, лежащие на ftp://ftp.lexa.ru/pub/apache-rus/ за исключением версий на основе Apache-1.1.x-1.2.x. Если у вас более старая версия - обновите ее.
6. Локализуйте ошибку - найдите минимальное подмножество файлов/скриптов при котором она проявляется.
7. Соберите в архив конфигурационные файлы сервера (httpd.conf,srm.conf,access.conf, Configuration /Configuration.apaci если вы собирали Apache-1.3.x через configure/) и локализованную ошибку. Если полученный архив превышает 100 килобайт в размере, то вы явно не локализовали ошибку.
8. Если используется Apache-RUS PL26 и новее, то соберите сервер с ключем компиляции -DRUSSIAN_APACHE_DEBUG (CFLAGS=-DRUSSIAN_APACHE_DEBUG ./configure ...) включите LogLevel debug и доложите в архив еще и трассировку ошибочного запроса из error_log
9. Если Apache падает в core, то скомпилируйте его с отладочной информацией (CFLAGS=-g) добейтесь еще раз core и посмотрите в отладчике где именно оно падает:
10. gdb httpd
11. (gdb) core /path/to/core
12. (gdb) where
13. ... выдача места падения

Выдачу gdb доложите в тот же архив.
14. Полученный архив пришлите автору.
Только при соблюдении такой последовательности реакция будет благожелательной. На письма вида "У меня ничего не перекодируется, убедись сам на www.somewhere.ru" я обычно реагирую отписками или не реагирую никак.

Обновлено: 13.03.2015