Я хочу переписать все http-запросы на моем веб-сервере на https-запросы, я начал со следующего:

server {
    listen      80;

    location / {
      rewrite     ^(.*)   https://mysite.com$1 permanent;
    }
...


Одна из проблем заключается в том, что при этом удаляется вся информация о поддомене (например, node1.mysite.com/folder), как я могу переписать приведенное выше, чтобы перенаправить все на https и сохранить поддомен?

answer

Правильный способ в новых версиях nginx

Оказалось, что мой первый ответ на этот вопрос был правильным в определенное время, но он превратился в другую ловушку - чтобы оставаться в курсе, пожалуйста, ознакомьтесь с ошибками при переписывании налогов.

Многие пользователи SE исправили меня, так что заслуга им, но, что более важно, вот правильный код:

server {
       listen         80;
       server_name    my.domain.com;
       return         301 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000" always; 

       [....]
}

ПРИМЕЧАНИЕ. Лучший способ сделать это был предоставлен https://serverfault.com/a/401632/3641, но он повторяется здесь:

server {
    listen         80;
    return 301 https://$host$request_uri;
}

В простейшем случае ваш хост будет зафиксирован как ваша служба, на которую вы хотите их отправить - это приведет к перенаправлению 301 в браузер, и URL-адрес браузера будет обновляться соответствующим образом.

Ниже приведен предыдущий ответ, который неэффективен из-за регулярного выражения, простой 301 отлично, как показано @kmindi

Я использовал nginx 0.8.39 и выше и использовал следующее:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

Посылает клиенту постоянное перенаправление.

Я думаю, что лучший и единственный способ - использовать перенаправление HTTP 301 Moved Permanently следующим образом:

server {
    listen         [::]:80;
    return 301 https://$host$request_uri;
}

HTTP 301 Moved Постоянно перенаправление также является наиболее эффективной , так как не существует регулярное выражения для оценки, в соответствии с уже упомянутой pitfails .


Новый HTTP 308 Moved Permanently сохраняет метод запроса и поддерживается основными браузерами . Например, использование 308запрещает браузерам изменять метод запроса с POSTна GETдля запроса перенаправления.


Если вы хотите сохранить имя хоста и поддомен, это способ.

Это все еще работает, если у вас нет DNS , поскольку я также использую его локально. Я запрашиваю, например, http://192.168.0.100/index.phpи буду перенаправлен точно на https://192.168.0.100/index.php.

Я использую listen [::]:80на своем хосте, потому что я bindv6onlyустановил false, поэтому он также привязывается к сокету ipv4. измените его на, listen 80если вам не нужен IPv6 или вы хотите привязать его где-нибудь еще.

В решении от Саифа Бечана используется локальный хост, server_nameкоторый в моем случае недоступен по сети.

Решение от Майкла Нила хорошее, но, судя по ямкам, есть лучшее решение с редиректом 301;)

В серверном блоке вы также можете делать следующее:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}

Вышеупомянутое не сработало с постоянным созданием новых поддоменов. например AAA.example.com BBB.example.com примерно для 30 поддоменов.

Наконец-то получил конфигурацию, работающую со следующим:

server {
  listen 80;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}
server {
  listen  443;
  server_name example.com;
  ssl on;
  ssl_certificate /etc/ssl/certs/myssl.crt;
  ssl_certificate_key /etc/ssl/private/myssl.key;
  ssl_prefer_server_ciphers       on;
# ...
# rest of config here
# ...
}

Я опубликовал комментарий к правильному ответу давным-давно с очень важным исправлением, но я считаю необходимым выделить это исправление в отдельном ответе. Ни один из предыдущих ответов не является безопасным для использования, если в какой-то момент у вас был настроен небезопасный HTTP и вы ожидаете пользовательский контент, имеете формы, размещаете API или настраивали какой-либо веб-сайт, инструмент, приложение или утилиту для общения с вашим сайтом.

Проблема возникает, когда POSTк вашему серверу поступает запрос. Если ответ сервера с простым 30xперенаправлением, содержимое POST будет потеряно. Что происходит, так это то, что браузер / клиент обновляет запрос до SSL, но понижает его POSTдо уровня GETзапроса. Эти POSTпараметры будут потеряны и некорректный будет сделан запрос на сервер.

Решение простое. Вам нужно использовать HTTP 1.1 307редирект. Это подробно описано в RFC 7231 S6.4.7:

  Note: This status code is similar to 302 (Found), except that it
  does not allow changing the request method from POST to GET.  This
  specification defines no equivalent counterpart for 301 (Moved
  Permanently) ([RFC7238], however, defines the status code 308
  (Permanent Redirect) for this purpose).

Решение, адаптированное из принятого решения, заключается в использовании 307в вашем коде перенаправления:

server {
       listen         80;
       server_name    my.domain.com;
       return         307 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000"; 

       [....]
}

У меня получилось вот так:

server {
listen 80;
listen 443 ssl;

server_name domain.tld www.domain.tld;

# global HTTP handler
if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
}

# global non-WWW HTTPS handler
if ($http_host = domain.tld){
        return 303 https://www.domain.tld$request_uri;
}
}

https://stackoverflow.com/a/36777526/6076984

Я запускаю ngnix за AWS ELB. ELB обращается к ngnix через http. Поскольку у ELB нет возможности отправлять перенаправления клиентам, я проверяю заголовок X-Forwarded-Proto и перенаправляю:

if ($http_x_forwarded_proto != 'https') {
    return 301 "https://www.exampl.com";
}

Если вы return 301 https://$host$request_uri;в качестве ответа по умолчанию используете порт 80, то ваш сервер может рано или поздно попасть в список открытых прокси [1] и начать злоупотреблять отправкой трафика в другое место в Интернете. Если ваши журналы заполнены сообщениями, подобными этому, вы знаете, что это случилось с вами:

42.232.104.114 - - [25/Mar/2018:04:50:49 +0000] "GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1" 301 185 "http://www.ioffer.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Hotbar 4.1.8.0; RogueCleaner; Alexa Toolbar)"

Проблема в том, что он $hostбудет отражать все, что браузер отправляет в Hostзаголовке или даже имя хоста из начальной строки HTTP, например:

GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1

Из-за этой проблемы некоторые другие ответы здесь рекомендуют использовать $server_nameвместо $host. $server_nameвсегда оценивает то, что вы указали в server_nameобъявлении. Но если у вас там несколько поддоменов или вы используете подстановочный знак, это не сработает, потому что $server_nameиспользуется только первая запись после server_nameобъявления и, что более важно, просто будет отражать подстановочный знак (не расширять его).

Итак, как поддерживать несколько доменов при сохранении безопасности? В моих собственных системах я справился с этой дилеммой, сначала перечислив default_serverблок, который не используется $host, а затем перечислил блок с подстановочными знаками, который:

server {
  listen 80 default_server;
  server_name example.com;
  return 301 https://example.com$request_uri;
}
server {
  listen 80;
  server_name *.example.com;
  return 301 https://$host$request_uri;
}

(Вы также можете указать более одного домена во втором блоке.)

С этой комбинацией несопоставленные домены будут перенаправлены куда-то жестко (всегда example.com), а домены, соответствующие вашему собственному, будут перенаправлены в нужное место. Ваш сервер не будет использоваться в качестве открытого прокси, поэтому вы не создадите проблем.

Если вы настроены злобно, я полагаю, вы также можете настроить default_serverблокировку, не совпадающую ни с одним из ваших законных доменов, и использовать что-то оскорбительное. . . .

[1] Технически «прокси» - неправильное слово, потому что ваш сервер не выходит и не выполняет запросы для клиентов, просто отправляя перенаправление, но я не уверен, какое слово будет правильным. Я также не уверен, какова цель, но она заполняет ваши журналы шумом и потребляет ваш процессор и полосу пропускания, так что вы можете положить этому конец.

Похоже, никто не понял это на 100% правильно. Чтобы запросы порта 80 направлялись к их эквивалентам 443 для всего веб-сервера, вам необходимо использовать директиву listen , а не директиву server_name, чтобы указать универсальное имя. См. Также https://nginx.org/en/docs/http/request_processing.html.

server {
    listen 80 default;
    listen [::]:80 default;
      return 307 https://$host$request_uri;
}
  • $ host перехватывает имена поддоменов.
  • 307 и 308 включают URI запросов POST и GET.
  • 307 - временный, после тщательного тестирования сменить на постоянный 308:

И убедитесь, что вы проверили, что уже находится в /etc/nginx/conf.d/, потому что чаще всего у меня возникали проблемы, когда default.conf возвращал какой-то существующий vhost. Мой порядок работы с проблемами nginx всегда начинается с перемещения файла по умолчанию, возвращая его, комментируя построчно, чтобы увидеть, где что-то пошло не так.

rewrite ^!https https://$host$request_uri permanent;