Назад | Перейти на главную страницу

Как в Nginx переписать все HTTP-запросы на https, сохранив поддомен?

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

server {
    listen      80;

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


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

Правильный способ в новых версиях 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 перемещен навсегда перенаправить так:

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

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


Новый HTTP 308 перемещен навсегда сохраняет метод Request и поддерживается основными браузерами. Например, используя 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 который в моем случае является localhost, но недоступен по сети.

Решение от Майкла Нила хорошее, но, судя по ямкам, есть лучшее решение с редиректом 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

Я запускаю nginx за AWS ELB. ELB обращается к nginx через 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] Технически «прокси» - неправильное слово, потому что ваш сервер не выходит и не выполняет запросы для клиентов, просто отправляя перенаправление, но я не уверен, какое слово будет правильным. Я также не уверен, какова цель, но он заполняет ваши журналы шумом и потребляет ваш процессор и пропускную способность, так что вы можете положить этому конец.

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

Похоже, никто не понял это на 100% правильно. Чтобы запросы порта 80 переходили к их эквивалентам 443 для всего веб-сервера, вам нужно использовать Слушать директива, а не директива 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 всегда начинается с перемещения файла по умолчанию, возврата его, построчно комментируя, чтобы увидеть, где что-то пошло не так.