Pertanyaan Di Nginx, bagaimana saya bisa menulis ulang semua permintaan http ke https sambil mempertahankan sub-domain?


Saya ingin menulis ulang semua permintaan http di server web saya untuk menjadi permintaan https, saya mulai dengan yang berikut:

server {
    dengarkan 80;

    lokasi / {
      tulis ulang ^ (. *) https: //mysite.com$1 permanen;
    }
...


Satu Masalah adalah bahwa ini menghapus semua informasi subdomain (mis., Node1.mysite.com/folder), bagaimana saya dapat menulis ulang di atas untuk mengubah rute semuanya ke https dan mempertahankan sub-domain?


495
2017-09-21 14:04




Silakan mempertimbangkan untuk memindahkan 'jawaban yang diterima' ke serverfault.com/a/171238/90758. Itu yang benar. - olafure
Cukup gunakan $ server_name, bukan hardcoded mysite.com - Fedir RYKHTIK


Jawaban:


Cara yang benar dalam versi baru nginx

Ternyata jawaban pertama saya untuk pertanyaan ini adalah benar pada waktu tertentu, tetapi itu berubah menjadi perangkap lain - untuk tetap up to date, silakan periksa Perpajakan menulis ulang jebakan

Saya telah dikoreksi oleh banyak pengguna SE, jadi kredit diberikan kepada mereka, tetapi yang lebih penting, ini adalah kode yang benar:

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; 

       [....]
}

737
2017-12-05 20:43



Anda harus melakukan ini pada domain dengan basis domain namun - tidak? Bagaimana jika Anda ingin menerapkannya ke setiap domain di server Anda? - JM4
@ JM4: jika Anda menggunakan $ host dalam penulisan ulang, bukan dari server_name dan menambahkan default_server ke direktif mendengarkan, ini akan bekerja untuk setiap domain di server Anda. - Klaas van Schelven
Sangat penting untuk menyebutkan bahwa 301 disimpan di cache lokal Anda tanpa tanggal kedaluwarsa. Tidak terlalu berguna ketika melakukan konfigurasi perubahan - Trefex
@everyone Gunakan pengalihan 307 untuk mempertahankan konten POST. - Mahmoud Al-Qudsi
Perhatikan bahwa Anda harus menggunakannya $host dari pada $server_name jika Anda menggunakan subdomain. - Catfish


CATATAN: Cara terbaik untuk melakukan ini disediakan oleh https://serverfault.com/a/401632/3641 - tetapi diulangi di sini:

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

Dalam kasus yang paling sederhana, host Anda akan diperbaiki menjadi layanan Anda yang ingin Anda kirimi mereka - ini akan melakukan pengalihan 301 ke browser dan URL browser akan diperbarui dengan semestinya.

Di bawah ini adalah jawaban sebelumnya, yang tidak efisien karena regex, 301 sederhana sangat bagus seperti yang ditunjukkan oleh @kmindi

Saya telah menggunakan nginx 0.8.39 ke atas, dan menggunakan yang berikut:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

Mengirim pengalihan permanen ke klien.


269
2017-08-17 03:07



Saya pikir itu harus 80 - karena ini mendengarkan http dan kemudian memberitahu klien untuk kembali sebagai https (443). - Michael Neale
Ini harus menjadi jawaban teratas! - Nathan
Ini adalah jawaban yang paling berat. - Case
ini adalah yang termudah, tetapi paling tidak aman - dengan cara ini Anda mengizinkan server Anda mengarahkan ulang pengguna ke halaman apa pun, tanpa memeriksa apakah itu bahkan diizinkan untuk digunakan di server Anda. Jika server Anda melayani mydomain.co, pengguna jahat masih dapat menggunakan server Anda untuk mengalihkan pengguna ke domain lain seperti mydomain.co, seperti google.com. - friedkiwi
@ cab0lt tidak ada masalah keamanan di sini. Melayani pengalihan tidak menunjukkan risiko keamanan. Jika ada persyaratan kontrol akses, mereka harus diperiksa pada titik di mana browser meminta URL baru. Peramban tidak akan mendapatkan akses hanya atas dasar pengalihan, juga tidak memerlukan pengalihan untuk meminta URL baru. - mc0e


Saya pikir cara terbaik dan satu-satunya harus menggunakan HTTP 301 Dipindahkan Secara Permanen redirect seperti ini:

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

Itu HTTP 301 Dipindahkan Secara Permanen redirect juga merupakan yang paling efisien karena tidak ada regex yang akan dievaluasi, menurut yang telah disebutkan pitfails.


Yang baru HTTP 308 Dipindahkan Secara Permanen mempertahankan metode Permintaan dan didukung oleh browser utama. Misalnya, menggunakan 308 mencegah peramban mengubah metode permintaan dari POST untuk GET untuk permintaan pengalihan.


Jika Anda menghendaki pertahankan nama host dan subdomain ini jalannya.

Ini masih berfungsi jika Anda tidak memiliki DNS, karena saya juga menggunakannya secara lokal. Saya meminta contoh dengan http://192.168.0.100/index.php dan akan dialihkan ke tepat https://192.168.0.100/index.php.

saya menggunakan listen [::]:80 pada host saya karena saya punya bindv6only mulai false, jadi itu juga mengikat ke soket IPv4. ubah menjadi listen 80 jika Anda tidak ingin IPv6 atau ingin mengikat di tempat lain.

Solusi dari Saif Bechan menggunakan server_name yang dalam kasus saya adalah localhost tetapi itu tidak dapat dijangkau melalui jaringan.

Solusi dari Michael Neale bagus, tetapi menurut pitfail, ada solusi yang lebih baik dengan redirect 301;)


121
2018-06-23 17:19



Bagus Anda mencoba mengutipnya, tetapi 301 tidak berfungsi pada HTTPS. - Case
apa yang tidak berhasil? bagian server yang dinyatakan adalah untuk trafik http (tanpa s) yang tidak dienkripsi untuk secara permanen dialihkan ke server terenkripsi (bagian yang mendengarkan pada 443 (https) tidak terdaftar) - kmindi
Saya memeriksa ini berfungsi baik dengan https dan semuanya - @kmindi Saya memperbarui jawaban saya dengan referensi Anda - karena menurut saya ini adalah cara yang benar dan ini terus bermunculan! Kerja bagus. - Michael Neale
Saat menggunakan permintaan domain (non-ip), tidak berfungsi kecuali saya mengubah '[::]: 80' menjadi '80'. - Joseph Lust
itu bisa menjadi perilaku yang diharapkan: trac.nginx.org/nginx/ticket/345. Saya memperbarui jawaban untuk menggambarkan opsi mendengarkan. - kmindi


Di atas tidak bekerja dengan dengan subdomain baru yang dibuat setiap saat. misalnya AAA.example.com BBB.example.com untuk sekitar 30 subdomain.

Akhirnya mendapat konfigurasi yang bekerja dengan yang berikut:

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
# ...
}

17
2018-06-25 04:29



Terima kasih! nginx akan kembali 301 https://*/ atau batalkan permintaan sebelum waktunya di jawaban yang lain di sini. server_name _; dengan $host adalah jawaban yang berhasil. +1 - zamnuts
Yang ini optimal! Namun, saya merekomendasikan beberapa untuk menggantikan _ dengan domain sebenarnya, mis. .domain.com Saya memiliki dua server, dan nginx secara tidak sengaja mengarahkan salah satu server saya ke server default. - zzz
Ini adalah satu-satunya jawaban yang berhasil untuk saya, terima kasih! - Snowman
Terima kasih banyak teman .. Saya telah mencoba banyak solusi tetapi tidak berhasil. Solusi ini luar biasa dan berhasil bagi saya. nama server _; apa artinya ini untuk ... aku tidak mengerti. Tolong jelaskan saya ini. - Pavan Kumar


Dalam blok server Anda juga dapat melakukan hal berikut:

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

16
2017-07-31 19:50



Konfigurasi ini menyebabkan server saya menghasilkan loop pengalihan - Corkscreewe
Mungkin karena ada pengalihan lain di tempat atau https tidak diaktifkan di situs / aplikasi Anda - Oriol
Tak satu pun dari yang lain tampaknya bekerja kecuali yang satu ini. Menggunakan versi nginx: nginx / 1.10.0 (Ubuntu) - ThatGuy343
upvoted untuk https: // $ host $ uri - AMB
Ini adalah cara untuk pergi jika Anda berada di belakang loadbalancer! - Antwan


Saya memposting komentar tentang jawaban yang benar sejak lama, lama lalu dengan koreksi yang sangat penting, tetapi saya merasa perlu untuk menyoroti koreksi ini dalam jawabannya sendiri. Tak satu pun dari jawaban sebelumnya yang aman untuk digunakan jika pada titik tertentu Anda memiliki pengaturan HTTP yang tidak aman dan mengharapkan konten pengguna, memiliki formulir, menginangi API, atau telah mengonfigurasi situs web, alat, aplikasi, atau utilitas apa pun untuk berbicara dengan situs Anda.

Masalah terjadi ketika a POST permintaan dibuat ke server Anda. Jika respon server dengan polos 30x mengalihkan konten POST akan hilang. Apa yang terjadi adalah browser / klien akan meningkatkan permintaan ke SSL, tetapi lakukan downgrade itu POST ke a GET permintaan. Itu POST parameter akan hilang dan permintaan yang salah akan dibuat ke server Anda.

Solusinya sederhana. Anda harus menggunakan HTTP 1.1 307 mengalihkan. Ini dijelaskan dalam 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).

Solusinya, diadaptasi dari solusi yang diterima, adalah untuk digunakan 307 di kode pengalihan Anda:

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

       [....]
}

6
2018-03-19 20:24



sangat, sangat perintah handfull. Terima kasih! - Denis Matafonov


Saya berhasil melakukannya seperti ini:

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


4
2018-04-21 18:27



Versi yang diperbarui, tanpa IF: paste.debian.net/plain/899679 - stamster


Saya menjalankan ngnix di belakang AWS ELB. ELB sedang berbicara dengan ngnix melalui http. Karena ELB tidak memiliki cara untuk mengirim pengalihan ke klien, saya memeriksa header X-Forwarded-Proto dan mengarahkan ulang:

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

3
2018-01-04 17:09





Jika kamu return 301 https://$host$request_uri; sebagai respons default pada port 80, maka server Anda mungkin cepat atau lambat akan mendapatkan daftar proxy terbuka [1] dan mulai disalahgunakan untuk mengirim lalu lintas ke tempat lain di Internet. Jika log Anda terisi dengan pesan seperti ini, maka Anda tahu itu terjadi pada Anda:

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)"

Masalahnya adalah $host akan menggemakan kembali apa pun yang dikirim browser di Host tajuk atau bahkan nama host dari baris pembuka HTTP, seperti ini:

GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1

Karena masalah itu, beberapa jawaban lain di sini menyarankan untuk menggunakan $server_name dari pada $host. $server_name selalu mengevaluasi apa kamu dimasukkan ke dalam server_name pernyataan. Tetapi jika Anda memiliki beberapa subdomain di sana atau menggunakan wildcard, itu tidak akan berfungsi, karena $server_name hanya menggunakan pertama entri setelah server_name deklarasi, dan yang lebih penting hanya akan menggemakan kembali wildcard (tidak memperluasnya).

Jadi bagaimana cara mendukung banyak domain sambil menjaga keamanan? Pada sistem saya sendiri, saya telah menghadapi dilema ini pertama daftar a default_server blok yang tidak digunakan $host, dan kemudian mencantumkan blok wildcard yang:

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

(Anda juga bisa mendaftar lebih dari satu domain di blok kedua.)

Dengan kombinasi itu, domain yang tidak cocok akan dialihkan ke suatu tempat yang sudah di-hardcoded (selalu example.com), dan domain yang cocok dengan Anda akan pergi ke tempat yang tepat. Server Anda tidak akan berguna sebagai proxy terbuka, jadi Anda tidak akan menarik masalah.

Jika Anda merasa kasar, saya kira Anda juga bisa membuatnya default_server blokir pertandingan tidak ada dari domain sah Anda dan melayani sesuatu yang ofensif. . . .

[1] Secara teknis "proxy" adalah kata yang salah, karena server Anda tidak keluar dan memenuhi permintaan untuk klien, hanya mengirim redirect, tapi saya tidak yakin kata apa yang tepat. Saya juga tidak yakin apa tujuannya, tetapi itu mengisi log Anda dengan noise dan mengkonsumsi CPU dan bandwidth Anda, jadi Anda mungkin juga menghentikannya.


0
2018-03-25 05:58