Вопрос: Как принудительно или перенаправлять SSL в nginx?


У меня есть страница регистрации в подобласти, например: https://signup.example.com

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

Мой блок html / server в nginx выглядит так:

html {
  server {
    listen 443;
    server_name signup.example.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}

Что я могу добавить, чтобы люди, которые http://signup.example.com перенаправить на https://signup.example.com ? (FYI Я знаю, что есть плагины Rails, которые могут заставить SSL но надеялся избежать этого)


205
2018-03-22 18:45


Источник


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


Ответы:


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

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}

135
2018-03-22 19:22



Или, согласно сайту, который вы связали, "ЛУЧШЕ": return 301 http://domain.com$request_uri; - nh2
один комментарий. $ server_name $ выбирает первую переменную server_name. Поэтому имейте это в виду, если у вас есть имена без FQN в вашей конфигурации - engineerDave
@ nh2 Это еще один случай неправильной документации, поскольку return 301... вызывает ошибку «слишком много переадресаций», в то время как метод перезаписи действительно работает. - Mike Bethany
Это теперь задокументировано как «также BAD». @MikeBethany return 301 работает, если (я думаю), вы его запускаете также для правильных URL-адресов, прослушивая оба порта (пример config. запускает проблему: возьмите serverfault.com/a/474345/29689's первый ответ и опустить if). - Blaisorblade
@Blaisorblade Спасибо, это намного лучший способ сделать это. - Mike Bethany


Лучший способ, описанный в официальное руководство является использование return директива:

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

234
2017-09-03 23:50



самый короткий ответ и отлично работал в моем случае - mateusz.fiolka
Это обычно рекомендуется, поскольку оно возвращает 301 Moved Permanently (ваши ссылки постоянно перемещаются), а также переписывание - sgb
Это не работает, так как вызывает «слишком много переадресаций», даже если вы установили proxy_set_header X-Forwarded-Proto https; - Mike Bethany
@MikeBethany вы определяете listen 443; в том же блоке? - Joe B
Это должен быть принятый ответ. - sjas


Это правильный и наиболее эффективный способ, если вы хотите сохранить все в одном блоке сервера:

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

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

Все остальное выше, используя «переписать» или «если ssl_protocol» и т. Д. Медленнее и хуже.

Здесь одно и то же, но даже более эффективно, только запустив переписывание по протоколу http, он избегает необходимости проверять переменную $ schem для каждого запроса. Но серьезно, это такая незначительная вещь, что вам не нужно их разделять.

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

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}

105
2018-01-31 20:43



Отлично, какой-то трус проголосовал за этот ответ, не сказав, почему, хотя этот ответ правильный. Может быть, еще один из тех, «злых», культистов. Если вы хотите прочитать документацию Nginx о If, вы узнаете, что IfIsNOTEvil, только CERTAIN использует ее в контексте {}, ни один из которых мы здесь делаем. Мой ответ - абсолютно правильный способ делать что-то! - DELETEDACC
Я не проголосовал за это, но хотел бы отметить, что в последних версиях значение по умолчанию было изменено на «default_server». - spuder
Первое решение не может быть наиболее эффективным, если второе является еще более эффективным. И вы даже описали, почему вы не должны использовать a, если есть: «это позволяет избежать проверки переменной $ schem для каждого запроса». Точка не использования ifs связана не только с производительностью, но и с декларативным, а не обязательным. - pepkin88
+1 для if ($ scheme = http) - Fernando Kosh
Здесь следует использовать $ host, как указано в других ответах. - Artem Russakovskii


Если вы используете новое определение двух HTTP и HTTPS-серверов, вы можете использовать следующее:

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

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}

Кажется, что это работает для меня и не вызывает перенаправление циклов.

Редактировать:

Заменены:

rewrite ^/(.*) https://$server_name/$1 permanent;

с перерисовкой Пратика.


56
2017-08-08 11:12



@DavidPashley ваше решение работало как прелесть для меня. благодаря - Jayesh Gopalan
If you are using the new dual HTTP and HTTPS server definition то вы должны отделить его. - VBart
элегантный и прекрасно работает! - jipipayo
Это было единственное решение, которое работало для меня с моей конфигурацией Laravel / Homestead Nginx. - Jared Eitnier
Кроме того, строка перезаписи должна быть return 301 https://$server_name$request_uri; так как это предпочтительный метод. - Jared Eitnier


Еще один вариант, который сохраняет заголовок Host: request и следует примеру «GOOD» на nginx ловушки:

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

    return 301 https://$host$request_uri;
}

Вот результаты. Обратите внимание, что использование $server_name вместо $host всегда будет перенаправляться на https://site1,

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux

26
2018-04-11 12:20



Note that using $server_name instead of $host would always redirect to https://site1 это не то, что $request_uri для? - Jürgen Paul
$request_uri не содержит имя хоста или домена. Другими словами, он всегда начинается с символа «/». - Peter
Лучший ответ. - Ashesh
Я не знаю, почему этот ответ настолько низок в голосах. Это единственное, что стоит использовать. - zopieux
Не могу поверить, что так много людей используют $ server_name, это правильный способ сделать это - Greg Ennis


Убедитесь, что вы установили «безопасный» на любые файлы cookie, иначе они будут отправлены по HTTP-запросу и могут быть схвачены с помощью такого инструмента, как Firesheep.


3
2018-03-23 00:40





server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}

Это работает лучше, я думаю. x.x.x.x относится к IP-адресу вашего сервера. Если вы работаете с Plesk 12, вы можете сделать это, изменив файл «nginx.conf» в каталоге «/var/www/vhosts/system/domain.tld/conf» для любого домена, который вы хотите. Не забудьте перезапустить службу nginx после сохранения конфигурации.


1
2017-08-23 19:40



rewrite ^ https://$host$request_uri? permanent;  было бы лучшим решением, поскольку у вас может быть несколько имен серверов на vhost


Я думаю, что это самое простое решение. Заставляет как HTTPS, так и не-WWW-трафик на HTTPS и только www.

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;
    }
}

EDIT - Apr 2018: Решение без IF's можно найти в моем сообщении здесь: https://stackoverflow.com/a/36777526/6076984


0
2018-04-21 18:30



Разве условия IF не считаются злыми и неэффективными в мире nginx? - PKHunter
Да, они вообще. Но для этих простых проверок я бы не догадался. У меня есть правильный файл конфигурации, который включает в себя больше написания кода, но позволяет полностью исключить IF. - stamster
Google рекомендует использовать 301 вместо 303. Источник: support.google.com/webmasters/answer/6073543?hl=en - Dylan Hunt
@DylanHunt - я оставил 303 только для тестирования, отметьте, что 1-й обработчик был настроен на 301, только 2-й я забыл изменить :) Кроме того, решение без IF: stackoverflow.com/a/36777526/6076984 - stamster