Як я можу переписати всі запити http на https, зберігаючи піддомен?


508

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

сервер {
    слухати 80;

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


Одна проблема полягає в тому, що це позбавляє будь-якої інформації про субдомен (наприклад, node1.mysite.com/folder), як я можу переписати вище, щоб перегрупувати все на https та підтримувати субдомен?


2
Подумайте про переміщення "прийнятої відповіді" на serverfault.com/a/171238/90758 . Це правильно.
olafure

Просто використовуйте $ server_name замість
жорсткого коду

Відповіді:


748

Правильний шлях у нових версіях nginx

Виявіть, моя перша відповідь на це питання була правильною в певний час, але це перетворилося на інший підводний камінь - щоб бути в курсі, будь ласка, перевірте, як переписати податки, переписати підводні камені

Мене виправили багато користувачів ДП, тому кредит їм належить, але що важливіше, ось правильний код:

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 $ для перезапису замість імені_сервера та додаєте default_server до директиви про прослуховування, він буде працювати для кожного домену вашого сервера.
Клаас ван Шельвен

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 таким чином:

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

HTTP 301 Moved постійно перенаправляти також є найбільш ефективним , так як не існує регулярний вираз для оцінки, в Відповідно до вже згаданого pitfails .


Новий HTTP 308 переміщений постійно зберігає метод запиту та підтримується основними браузерами . Наприклад, використання 308перешкоджає браузерам змінювати метод запиту з POSTна GETзапит на переадресацію.


Якщо ви хочете зберегти ім'я хоста та піддомен, це такий шлях.

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

Я використовую listen [::]:80на своєму хості, тому що я bindv6onlyвстановив false, тому він також прив'язується до sop ipv4. змініть його на, listen 80якщо ви не хочете IPv6 або хочете зв’язувати його в іншому місці.

Рішення від Saif Bechan використовує те, server_nameщо в моєму випадку є localhost, але це недоступно через мережу.

Рішення від Майкла Ніла добре, але згідно з підводними повідомленнями, є краще рішення з переадресацією 301;)


Приємно, що ви намагаєтесь його процитувати, але 301 не працює на HTTPS.
Справа

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

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

Якщо ви користуєтесь запитом домену (non-ip), він не працює, якщо я не зміню '[::]: 80' на '80'.
Йосип Похоть

така може бути очікувана поведінка: trac.nginx.org/nginx/ticket/345 . Я оновив відповідь, щоб описати варіант прослуховування.
kmindi

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
Це шлях, якщо ви стоїте за вантажником!
Антван

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
zamnuts

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

Мені вдалося це зробити так:

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


Оновлена ​​версія, без ІЧ
stamster

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 еквівалентів для цілого веб-сервера, вам потрібно використовувати директиву прослуховування , а не директиву імені сервера, щоб вказати ім'я загального доступу. Дивіться також https://nginx.org/en/docs/http/request_processing.html

сервер {
    прослухати 80 за замовчуванням;
    прослухати [::]: 80 за замовчуванням;
      повернути 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.