В Nginx, как я могу переписать все http-запросы в https при сохранении субдомена?


509

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

сервер {
    слушай 80;

    место нахождения / {
      переписать ^ (. *) https: //mysite.com$1 постоянный;
    }
...


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


2
Пожалуйста, рассмотрите возможность перемещения «принятого ответа» на serverfault.com/a/171238/90758 . Это правильный.
olafure

Просто используйте $ server_name вместо hardcoded mysite.com
Федир РИХТИК,

Ответы:


749

Правильный путь в новых версиях 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; 

       [....]
}

3
Вы должны сделать это на доменной основе, однако - нет? Что если вы хотите применить его к каждому домену на вашем сервере?
JM4

30
@ JM4: если вы используете $ host $ в перезаписи вместо server_name и добавляете default_server в директиву listen, это будет работать для каждого домена на вашем сервере.
Клаас ван Шельвен

5
Важно отметить, что 301 хранится в вашем локальном кэше без даты истечения срока действия. Не очень полезно при изменении конфигурации
Trefex

9
@everyone Используйте редирект 307 для сохранения содержимого POST.
Махмуд Аль-Кудси

10
Обратите внимание, что вы должны использовать $hostвместо, $server_nameесли вы используете поддоменов.
сом

276

ПРИМЕЧАНИЕ. Лучший способ сделать это был предоставлен 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;
 }

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


15
Я думаю, что это должно быть 80 - поскольку это слушает http и затем говорит клиенту возвращаться как https (443).
Майкл Нил

3
Это должен быть главный ответ!
Натан

3
Это самый сложный ответ.
Дело

1
это самый простой, но наименее безопасный способ, позволяющий вашему серверу перенаправлять пользователя на любую страницу, не проверяя, разрешено ли его использование на вашем сервере. Если ваш сервер обслуживает mydomain.co, злоумышленники могут по-прежнему использовать ваш сервер для перенаправления пользователей в другие домены, например mydomain.co, например, google.com.
friedkiwi

10
@ cab0lt здесь нет проблем с безопасностью. Обслуживание перенаправления не представляет угрозы безопасности. Если есть требования к контролю доступа, они должны быть проверены в точке, где браузер запрашивает новый URL. Браузер не получит доступ просто на основе перенаправления, и при этом они не нуждаются в перенаправлении, чтобы запросить новый URL.
mc0e

125

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

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

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


Новый HTTP 308 Moved Permanently сохраняет метод 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 или хотите связать в другом месте.

Решение от Saif Bechan использует то, server_nameчто в моем случае является localhost, но оно недоступно по сети.

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


Хорошо, что вы пытаетесь процитировать его, но 301 не работает на HTTPS.
Дело

5
что не работает? указанный раздел сервера предназначен для перенаправления незашифрованного трафика http (без s) на зашифрованный сервер (этот раздел, который прослушивает 443 (https), не указан)
kmindi

Я проверил, что это отлично работает с https и все - @kmindi Я обновил свой ответ со ссылкой на ваш - так как я думаю, что это правильный путь, и это продолжает появляться! Хорошо сделано.
Майкл Нил

При использовании запроса домена (не ip) не работает, если я не изменю '[::]: 80' на '80'.
Джозеф Луст

это может быть ожидаемое поведение: trac.nginx.org/nginx/ticket/345 . Я обновил ответ, чтобы описать опцию прослушивания.
Кинди

20

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

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

2
Эта конфигурация заставила мой сервер произвести цикл перенаправления
Corkscreewe

Возможно, причина в том, что имеется другое перенаправление или https не включен на вашем сайте / в приложении
Oriol

1
Никто из остальных, казалось, не работал, кроме этого. Использование версии nginx: nginx / 1.10.0 (Ubuntu)
ThatGuy343

проголосовал за https: // $ host $ uri
AMB

4
Это путь, если вы находитесь за балансиром нагрузки!
Antwan

17

Вышесказанное не работает с новыми поддоменами, которые создаются постоянно. например, 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
# ...
}

благодарю вас! nginx либо возвратит, 301 https://*/либо отменит запрос преждевременно в других ответах здесь. server_name _;с $hostбыл ответ, который добился цели. +1
замнулс

1
Этот оптимален! Тем не менее, я рекомендую некоторым заменить _его фактическим доменом, например, у .domain.comменя было два сервера, и nginx случайно перенаправил один из моих серверов на сервер по умолчанию.
zzz

1
Это единственный ответ, который сработал для меня, спасибо!
Снеговик

Спасибо большое, приятель. Я перепробовал многие решения, но ничего не получилось. Это великолепное решение, и оно сработало для меня. название сервера _; для чего это .. я не поняла. Пожалуйста, объясните мне это.
Паван Кумар

6

Я написал комментарий к правильному ответу давным-давно с очень важной поправкой, но я чувствую, что необходимо выделить эту поправку в своем собственном ответе. Ни один из предыдущих ответов не является безопасным для использования, если в какой-то момент вы настроили небезопасный 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"; 

       [....]
}


4

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

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

1

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


0

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

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

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


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.