Application: Nginx.
Вероятно читатель этой заметки сталкивался с особенностями настройки web-серверов (Nginx, Apache), когда на обращения к серверу прямо по IP-адресу, без указания доменного имени, выдавался первый описанный в конфигурации сервера сайт, что не совсем соответствовало намерениям владельца ресурса.
Это нормальное поведение Nginx (да и Apache тоже - но здесь не о нём), который определяя, какому виртуальному web-серверу следует направить запрос, проверяет только поле "Host" заголовка запроса. Если его значение не соответствует ни одному из имён виртуальных web-серверов или в заголовке запроса нет этого поля вовсе, Nginx направит запрос в виртуальный web-сервер "по умолчанию" для этого порта, которым, если этого явно не обозначено, является первый упомянутый в конфигурационных файлах. Определение "первый упомянутый" нечёткое и на практике не всегда срабатывающее ожидаемым образом; избежать его использования можно явно определив какой-либо виртуальный web-сервер обработчиком запросов неприменимых к другим web-серверам.
Директива "listen" в описании виртуального web-сервера поддерживает параметр "default_server" (в версиях Nginx до 0.8.21 используется форма "default"), указывающая на особую роль этого виртуального web-сервера как обработчика всех не нашедших адресата запросов. Следует иметь в виду, что опция "default_server" задаёт свойства слушающего порта (сочетания IP адреса и IP-порта, который прослушивает Nginx), а не виртуального web-сервера, обслуживаемого Nginx; иначе говоря, мы указываем, что этот виртуальный web-сервер является обработчиком "по умолчанию" для какого-то определённого сетевого интерфейса, в то время как другому интерфейсу может быть назначен иной обработчик. Впрочем, если настроить Nginx на прослушивание всех интерфейсов одновременно, то и виртуальный web-сервер "по умолчанию" может быть только один.
Ещё в Nginx имеется возможность указать "пустое" имя сервера (начиная с Nginx версии 0.7.11). В документации указано, что это позволяет обрабатывать запросы без поля "Host" заголовка запроса в целевом виртуальном web-сервере. Для пущей эффективности, можно использовать и этот функционал.
Создаём для Nginx конфигурацию виртуального web-сервера, которая будет служить нам для перехвата запросов по IP-адресу без указания параметра "Host" и перенаправления таковых на заданное доменное имя "example.net":
# touch /etc/nginx/sites-available/no-host
server {
listen *:80 default_server;
server_name localhost "";
rewrite ^(.*)$ http://example.net/ permanent;
}
server {
listen *:443 default_server;
server_name localhost "";
....
# Блок описания параметров установления SSL-сессии (помним, что защищённое соединение устанавливается до пересылки заголовков HTTP-запроса, потому оно должно состоятся даже только для того, чтобы сразу после этого быть сброшенным)
....
rewrite ^(.*)$ https://example.net/ permanent;
}
listen *:80 default_server;
server_name localhost "";
rewrite ^(.*)$ http://example.net/ permanent;
}
server {
listen *:443 default_server;
server_name localhost "";
....
# Блок описания параметров установления SSL-сессии (помним, что защищённое соединение устанавливается до пересылки заголовков HTTP-запроса, потому оно должно состоятся даже только для того, чтобы сразу после этого быть сброшенным)
....
rewrite ^(.*)$ https://example.net/ permanent;
}
Здесь мы создали виртуальный web-сервер, обслуживающий доменное имя "localhost" (некому просматривать сайт прямо с сервера), прибавив к нему обслуживание "пустого" имени (эквиваленту запроса без параметра "Host") и объявив в придачу этот виртуальный web-сервер обработчиком запросов "по умолчанию" для всех сетевых интерфейсов сервера.
Обращаю внимание на нюанс настройки: просто "пустое" имя как опция параметра "server_name" у меня не применялось, требовалось указывать его в дополнение к какому-нибудь явному имени сервера, вроде "localhost".
Активируем конфигурацию символической ссылкой и даём Nginx команду применить её:
# ln -s /etc/nginx/sites-available/no-host /etc/nginx/sites-enabled/no-host
# /etc/init.d/nginx reload
# /etc/init.d/nginx reload
На самом деле перенаправление всех нецелевых запросов на реальный web-сервер не всегда хорошая идея. С одной стороны это вроде как повышает доступность ресурса, а с другой стороны даёт возможность загрузить сервер бессмысленной работой, обрабатывая трафик генерируемый разного рода "ботами" и "сканерами". Я предпочитаю не перенаправлять нецелевые запросы, а отлавливать их и не отвечать на них вообще.
Nginx может прерывать обработку соединения, закрывая его без передачи клиенту заголовка ответа. Скорректируем конфигурацию, указав отдавать в ответ на запрос код "444" (который на самом деле не "отдаётся", а служит модификатором нестандартного поведения Nginx):
server {
listen *:80 default_server;
server_name localhost "";
return 444;
}
listen *:80 default_server;
server_name localhost "";
return 444;
}
Теперь при запросе ресурса по IP-адресу соединение будет прерываться без ответа. В некоторых случаях браузеры могут проинформировать клиента о необычном поведении сервера следующим образом:
Zero Sized Reply: application did not receive any data for this request.
Материалы, используемые в работе: