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

Вопрос или проблема

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

server {
    listen      80;

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

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

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

Выяснилось, что мой первый ответ на этот вопрос был правильным в определенное время, но превратился в очередную ловушку – чтобы оставаться в курсе, пожалуйста, проверьте Taxing rewrite pitfalls

## Перенаправление всего HTTP-трафика на HTTPS
server {
  listen          [::]:80 default_server;
  listen               80 default_server;
  server_name     _;
  return          307 https://$host$request_uri;
}

server {
  listen         443 ssl http2 quic;
  server_name    my.domain.com;

  # добавьте Strict-Transport-Security для предотвращения атак типа "человек посередине"
  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 Permanently также является самым эффективным, потому что нет необходимости оценивать регулярные выражения, согласно уже упомянутым ловушкам.


Новая 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 или хотите привязать к другому месту.

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

Решение от Michael Neale хорошее, но согласно ловушкам, есть лучшее решение с редиректом 301 😉

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

# Принудительное HTTPS-соединение. Это правило не зависит от домена
if ($scheme != "https") {
    rewrite ^ https://$host$request_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;
# ...
# оставшаяся конфигурация здесь
# ...
}

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

Проблема возникает, когда делается POST запрос к вашему серверу. Если сервер отвечает простым 301/302 редиректом содержимое POST будет потеряно. Происходит следующее: браузер/клиент обновит запрос до SSL, но понизит POST до GET запроса. Параметры POST будут утеряны и будет осуществлен неверный запрос к вашему серверу.

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

  Примечание: Этот код состояния похож на 302 (Found), за исключением того,
  что он не допускает изменения метода запроса с POST на GET. 
  Эта спецификация не определяет эквивалентного аналога для 301 (Moved
  Permanently) ([RFC7238], однако, определяет код состояния 308
  (Permanent Redirect) для этой цели).

Решение, адаптированное из принятого решения, заключается в использовании 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;
       # добавьте Strict-Transport-Security для предотвращения атак типа "человек посередине"
       add_header Strict-Transport-Security "max-age=31536000"; 

       [....]
}

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

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

Я смог это сделать так:

server {
listen 80;
listen 443 ssl;

server_name domain.tld www.domain.tld;

# глобальный обработчик HTTP
if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
}

# глобальный обработчик non-WWW HTTPS
if ($http_host = domain.tld){
        return 303 https://www.domain.tld$request_uri;
}
}

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

Если вы 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 включают как запросы POST, так и GET.
  • 307 временный, измените на постоянный 308 после тщательного тестирования:

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

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

.

Ответ или решение

Чтобы перенаправить все HTTP-запросы на HTTPS на вашем веб-сервере Nginx и при этом сохранить информацию о поддоменах, вам нужно настроить конфигурацию Nginx соответствующим образом. Рассмотрим этот процесс детально.

Основная проблема и её решение

Основная задача заключается в переадресации всех запросов с порта 80 (HTTP) на порт 443 (HTTPS), удаляя при этом уязвимости, связанные с возможным изменением метода запроса, и обеспечивая сохранение поддоменов.

Конфигурация Nginx

Вот корректная конфигурация для перенаправления HTTP-запросов на HTTPS с минимальными накладными расходами и с сохранением поддоменов:

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # Переадресация всех HTTP-запросов на HTTPS, используя поддомен и правый URI
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name ваш.домен.*; # Укажите ваш домен

    # Настройки SSL
    ssl_certificate /путь/к/cert.crt;
    ssl_certificate_key /путь/к/privkey.key;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=31536000" always;

    # Остальная часть конфигурации...
}

Объяснение настройки

  1. Слушатель для HTTP: Первый server блок прослушивает 80 порт и перенаправляет все запросы на соответствующий HTTPS URL-адрес, сохраняя поддомен и путь URI. Перенаправление с кодом 301 — это постоянное перенаправление, что является SEO-дружественным решением, и браузер обновляет адрес в адресной строке, запоминая новое местоположение.

  2. Использование $host и $request_uri: Эти переменные обеспечивают сохранность поддоменов и исходного URI в процессе перенаправления.

  3. Настройки SSL: Второй server блок обслуживает HTTPS-запросы. Тут включены SSL-сертификаты и директрива Strict-Transport-Security для защиты пользователей от атак «человек посередине».

  4. Оптимизация производительности и безопасности: Использование прямого перенаправления return вместо rewrite снижает нагрузку на сервер, так как не применяется дополнительная обработка выражений.

Заключение

Рассмотренный выше метод является хорошей практикой для обеспечения безопасного доступа к веб-сайтам через HTTPS, что особенно важно в текущих условиях кибербезопасности. Правильная конфигурация вашего веб-сервера Nginx способствует поддержанию как безопасности, так и оптимальной производительности ресурсов.

Следуя изложенным рекомендациям, вы сможете обеспечить ваше сетевое приложение целостным решением для перенаправления трафика, которое будет поддерживать поддомены, улучшать SEO-оптимизацию и защищать данные пользователей.

Оцените материал
Добавить комментарий

Капча загружается...