Я пытаюсь перенаправить http://www.example.com в Nginx. Сертификат установлен только для версии без www. У меня есть два правила перенаправления 301 в таком порядке:
1) С http://example.com на
server {
listen 80;
listen 443 ssl;
server_name example.com;
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
}
2) С http://WWW.example.com на http://example.com
server {
listen 80;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
Когда я запрашиваю www.example.com через curl с терминала, я вижу:
HTTP/1.1 301 Moved Permanently
Location: http://example.com/
Это именно то, что я ожидал. Однако, когда я делаю то же самое в браузере, он перенаправляет меня и выдает ошибку сертификата.
Я исправил это, добавив сертификат в www-версию, но мне интересно, почему он не работал без него? И почему curl дает другой результат?
На всякий случай, параметры SSL, которые я использую:
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 5m;
ssl_dhparam /etc/nginx/pki/dhparam.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
resolver 8.8.8.8;
ssl_stapling on;
add_header Strict-Transport-Security "max-age=31536000";
Для очищенной дискуссии предлагаю разделить всю логику nginx на основной узел (example.conf config) и "сателлиты" (www_example.conf и т.д.). Спутники не должны иметь огромную логику.
Если в главном узле вы перенаправляете весь HTTP-трафик на HTTPS, - я предлагаю сделать это и в сателлитах - это немного быстрее:
www_example. conf
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
Я изменил $scheme
на https
.
Тогда, если я правильно понимаю вас - у вас две проблемы.
1. CURL не следует за вашими перенаправление. По умолчанию он этого не делает, вы должны пройти -L
:
curl "http://www.example.com" -L
2. Еще одна проблема заключается в том, как nginx разбирает SSL-соединения. SSL не проверяет на уровне "config сервера". Он проверяет задолго до того, как сервер получит пакеты данных соединения и начнёт выполнять хендшейк - таким образом, ваши конфигы с именем_сервера во время этой проверки не используются.
Когда ваш сервер получает входящее соединение для SSL-хендшейка, он получает первый конфиг, который прослушивает текущий ip:port
и пытается сделать хендшейк с ключом, цепочкой и сертификатом ssl.
Итоги:
Если вы хотите передать "https://www.example.com" и получить ответ с "https://example.com" - у вас должны быть оба сервера, настроенные на SSL.
Если вы хотите передать "http://www.example.com" и получить ответ с "https://example.com", вы должны быть уверены, что рукопожатие работает только на example.com, а не перед входом в этот домен. Как "разрешающие шаги" вашего сервера вы можете проверить через вывод wget
- это самый простой способ.
Здесь вы можете найти мой конфигурационный файл, который я использовал для проверки вашей проблемы:
server {
listen 80;
listen 443 ssl;
server_name example.com;
ssl on;
ssl_certificate /path/to.crt;
ssl_certificate_key /path/to.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
location / {
add_header Content-type text/plain;
return 200 "OK";
}
}
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
я добавил для отладки раздел location
с return 200 "OK"
. Затем в CMD:
$ curl "http://www.example.com" -L
OK
$ wget "http://www.example.com" -O /dev/null
--2016-11-24 15:54:33-- http://www.example.com/
Resolving www.example.com (www.example.com)... 127.0.0.1
Connecting to www.example.com (www.example.com)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://example.com/ [following]
--2016-11-24 15:54:33-- https://example.com/
Resolving example.com (example.com)... 127.0.0.1
Connecting to example.com (example.com)|127.0.0.1|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2 [application/octet-stream]
В последнем выводе видно, что "шаги по разрешению" работают корректно.
В браузере тоже все работает корректно.
. Если сайт www.example.com
ранее обслуживался с заголовком Strict-Transport-Security
(или includeSubdomains
) на примере . com
) - браузер запомнит (на долгое время - например, 31536000 секунд) и всегда будет продвигать http
подключения к https
еще до того, как получит доступ к www.example.com
.
Другое решение, использующее модуль map .
В этом случае требуется только одна серверная директива для http (хотя это может быть достигнуто с помощью жесткого- закодированный хост в предложении return
). Кроме того, это позволяет повторно использовать полученную переменную $ root_domain из модуля карты в любом виртуальном хосте и инструментах автоматизации. А также нет необходимости в директиве if.
Итак, для получения «чистого» имени хоста:
map $host $root_domain {
default none;
~*^www\.(.*)$ $1;
~*(.*) $1;
}
В nginx.conf
А затем для HTTP:
server {
listen 80;
server_name .example.org;
return 301 https://$root_domain$request_uri;
}
и HTTPS:
server {
listen 443 ssl http2;
ssl_certificate ...;
ssl_certificate_key ...;
server_name www.example.org;
return 301 https://$domain_root$request_uri;
}