Доброго дня сообществу! Прошу совета по проблеме.
Собственно, редирект с www на https без-www

server {
    #1 http to https without www
    listen 1.2.3.4:80;
    server_name www.test.com test.com;
    rewrite ^ https://test.com$request_uri? permanent;
}

server {
    #2 https with www to https without www
    listen 1.2.3.4:443 ssl;
    server_name www.test.com;
    rewrite ^ https://test.com$request_uri? permanent;
}

server {
    #3 https without www
    listen 1.2.3.4:443 ssl;
    server_name test.com;
    ...
}

Насколько я понял из документации 
(http://nginx.org/ru/docs/http/ngx_http_core_module.html#var_host), переменная 
$host принимает значения "в порядке приоритета: имя хоста из строки запроса, 
или имя хоста из поля Host заголовка запроса, или имя сервера, соответствующего 
запросу"

Судя по логам, это не совсем так.
По крайней мере, в моем случае (nginx/1.10.2), переменная $host получает имя 
хоста из строки запроса только если не указано поле host заголовка. Т.е. 
обрабатывается ситуация с HTTP/1.0, без $http_host в заголовке. Но если в 
заголовке задать какое-то (любое) значение $http_host, это же значение получает 
и $host.

Далее (http://nginx.org/ru/docs/http/request_processing.html): nginx 
"сопоставляет значение поля Host заголовка запроса с директивами server_name в 
блоках server, которые соответствуют IP-адресу и порту". Т.е. все-таки 
$http_host. А туда можно прописать что угодно.
Я не рассматриваю сейчас ситуацию, когда в $http_host прописано имя, не 
совпадающее с перечисленными в server_name. Это все благополучно фильтруется и 
отправляется на 444. Также, я не рассматриваю браузеры, которые отправляют 
правильный $http_host и получают правильные редиректы.

Вот такой случай:
curl -ILH 'Host: www.test.com' https://test.com

Если бы переменная $host получила значение в порядке приоритета, оно было бы 
test.com (имя хоста из строки запроса). Тогда можно было бы реализовать такой 
костыль, как фильтрация по условию "$host не равно $http_host". Но в запросе 
присутствует заголовок host, и обе переменные $host и $http_host получают одно 
и то же значение www.test.com , отфильтровать невозможно.
Имя www.test.com перечислено в server_name, в итоге такой запрос успешно 
проходит фильтрацию. После сопоставления значения $http_host с server_name, 
nginx отправляет запрос в секцию 2, откуда возвращается 301 и новый location 
https://test.com , т.е. на выходе получаем тот же самый запрос (curl -ILH 
'Host: www.test.com' https://test.com) и, естесвенно, зацикливание на второй 
секции:

curl -ILH 'Host: www.test.com' https://test.com
HTTP/1.1 301 Moved Permanently
..........
Location: https://test.com/

HTTP/1.1 301 Moved Permanently
..........
Location: https://test.com/

..........

Это баг или фича? Или я что-то делаю не так? Как это побороть?
Спасибо.
--
_______________________________________________
nginx-ru mailing list
nginx-ru@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-ru

Ответить