Pertanyaan Bagaimana cara memaksa atau mengalihkan ke SSL di nginx?


Saya memiliki halaman pendaftaran di subdomain seperti: https://signup.example.com

Seharusnya hanya dapat diakses melalui HTTPS tapi saya khawatir orang-orang entah bagaimana mungkin tersandung melalui HTTP dan mendapatkan 404.

Blok html / server saya di nginx terlihat seperti ini:

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

Apa yang bisa saya tambahkan agar orang yang pergi ke sana http://signup.example.com dialihkan ke https://signup.example.com ? (FYI saya tahu ada plugin Rails yang dapat memaksa SSL tetapi berharap untuk menghindari itu)


210
2018-03-22 18:45




Kemungkinan duplikat dari Di Nginx, bagaimana saya bisa menulis ulang semua permintaan http ke https sambil mempertahankan sub-domain? - Nasreddine


Jawaban:


Menurut jebakan nginx, itu sedikit lebih baik untuk menghilangkan penangkapan yang tidak perlu, menggunakan $request_uri sebagai gantinya. Dalam hal ini, tambahkan tanda tanya untuk mencegah nginx menggandakan argumen kueri.

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

137
2018-03-22 19:22



Atau, menurut situs yang Anda tautkan, "LEBIH BAIK": return 301 http://domain.com$request_uri; - nh2
satu komentar. $ server_name $ mengambil variabel server_name pertama. Jadi sadarilah ini jika Anda memiliki nama non FQN di konfigurasi Anda - engineerDave
@ nh2 Ini adalah kasus lain dari dokumentasi yang salah sejak digunakan return 301... menyebabkan kesalahan "terlalu banyak pengalihan" sementara metode penulisan ulang benar-benar berfungsi. - Mike Bethany
Itu sekarang didokumentasikan sebagai "juga BURUK". @MikeBethany return 301 tidak berfungsi, kecuali (saya kira) Anda memicu itu juga untuk URL yang benar, dengan mendengarkan pada kedua port (contoh konfigurasi yang memicu masalah: ambil serverfault.com/a/474345/29689's jawab dulu dan hilangkan jika). - Blaisorblade
Saya bertanya-tanya apa yang telah berubah selama bertahun-tahun dan apakah jawaban lain ini lebih baik: serverfault.com/a/337893/119666 - Ryan


Cara terbaik seperti yang dijelaskan di bagaimana cara melakukannya adalah dengan menggunakan return direktif:

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

237
2017-09-03 23:50



jawaban terpendek dan bekerja dengan sempurna dalam kasus saya - mateusz.fiolka
Ini umumnya disarankan karena mengembalikan a 301 Moved Permanently (tautan Anda telah dipindahkan secara permanen) serta penulisan ulang - sgb
Ini tidak berfungsi karena menyebabkan kesalahan "terlalu banyak pengalihan" meskipun Anda telah mengaturnya proxy_set_header X-Forwarded-Proto https; - Mike Bethany
@MikeBethany apakah Anda mendefinisikan listen 443; di blok yang sama? - Joe B
Ini harus menjadi jawaban yang diterima. - sjas


Ini adalah cara yang benar dan paling efisien jika Anda ingin menyimpannya dalam satu blok server:

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

Segala sesuatu yang lain di atas, menggunakan "menulis ulang" atau "jika ssl_protocol" dll lebih lambat dan lebih buruk.

Di sini adalah sama, tetapi bahkan lebih efisien, dengan hanya menjalankan penulisan ulang pada protokol http itu menghindari harus memeriksa variabel $ scheme pada setiap permintaan. Tapi serius, itu hal yang sangat kecil sehingga Anda tidak perlu memisahkan mereka.

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

108
2018-01-31 20:43



Hebat, beberapa pengecut memilih jawaban ini tanpa mengatakan alasannya, meskipun jawaban ini benar. Mungkin salah satu dari mereka adalah "sekte jahat". Jika Anda repot-repot membaca dokumentasi Nginx tentang Jika, Anda akan tahu bahwa IfIsNOTEvil, hanya menggunakan CERTAIN dalam konteks lokasi {}, tidak ada yang kita lakukan di sini. Jawaban saya adalah cara yang benar untuk melakukan sesuatu! - DELETEDACC
Saya tidak memilih ini, tetapi saya ingin menunjukkan bahwa default telah diubah menjadi 'default_server' dalam versi terbaru. - spuder
Solusi pertama tidak dapat menjadi yang paling efisien, jika yang kedua bahkan lebih efisien. Dan Anda bahkan menjelaskan, mengapa Anda tidak harus menggunakan jika ada: "itu menghindari keharusan untuk memeriksa variabel skema $ pada setiap permintaan". Intinya tidak menggunakan ifs bukan hanya tentang kinerja, tetapi juga tentang deklaratif, dan bukan keharusan. - pepkin88
+1 untuk if ($ scheme = http) - Fernando Kosh
Sebaiknya gunakan $ host di sini, seperti yang disebutkan dalam jawaban lainnya. - Artem Russakovskii


Jika Anda menggunakan definisi server HTTP dan HTTPS ganda yang baru, Anda dapat menggunakan yang berikut:

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

Ini tampaknya berfungsi untuk saya dan tidak menyebabkan loop pengalihan.

Edit:

Diganti:

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

dengan garis penulisan ulang Pratik.


56
2017-08-08 11:12



@DavidPashley, solusi Anda bekerja seperti pesona bagi saya. Terima kasih - Jayesh Gopalan
If you are using the new dual HTTP and HTTPS server definition maka Anda harus memisahkannya. - VBart
elegan dan bekerja sempurna! - jipipayo
Ini adalah satu-satunya solusi yang berhasil bagi saya dengan konfigurasi Laravel / Homestead Nginx saya. - Jared Eitnier
Juga garis penulisan ulang seharusnya return 301 https://$server_name$request_uri; karena ini adalah metode yang disukai. - Jared Eitnier


Namun varian lain, yang menjaga Host: meminta tajuk dan mengikuti contoh "BAIK" di jebakan 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;
}

Inilah hasilnya. Perhatikan bahwa menggunakan $server_name dari pada $host akan selalu mengalihkan ke 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

27
2018-04-11 12:20



Note that using $server_name instead of $host would always redirect to https://site1 bukankah itu apa $request_uri adalah untuk? - Jürgen Paul
$request_uri tidak mengandung host atau nama domain. Dengan kata lain, selalu dimulai dengan karakter "/". - Peter
Jawaban terbaik sejauh ini. - Ashesh
Saya tidak yakin mengapa jawaban ini sangat rendah. Itu satu-satunya yang layak digunakan. - zopieux
Tidak bisa percaya begitu banyak orang menggunakan $ server_name ini adalah cara yang benar untuk melakukannya - Greg Ennis


Pastikan Anda mengatur 'aman' pada cookie apa pun, atau mereka akan dikirim pada permintaan HTTP dan dapat diraih oleh alat seperti 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;
}

Ini bekerja lebih baik menurutku. x.x.x.x mengacu pada IP server Anda. Jika Anda bekerja dengan Plesk 12, Anda dapat melakukannya dengan mengubah file "nginx.conf" di direktori "/var/www/vhosts/system/domain.tld/conf" untuk domain mana pun yang Anda inginkan. Jangan lupa untuk memulai kembali layanan nginx setelah Anda menyimpan konfigurasi.


1
2017-08-23 19:40



rewrite ^ https://$host$request_uri? permanent;  akan menjadi solusi yang lebih baik karena Anda mungkin memiliki beberapa nama server pada vhost


Saya pikir ini adalah solusi paling sederhana. Memaksakan traffic non-HTTPS dan non-WWW ke HTTPS dan hanya 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: Solusi tanpa IF dapat ditemukan di posting saya di sini: https://stackoverflow.com/a/36777526/6076984


0
2018-04-21 18:30



Bukankah kondisi IF dianggap jahat dan tidak efisien di dunia nginx? - PKHunter
Ya, pada umumnya. Tetapi untuk pemeriksaan sederhana ini saya kira tidak. Saya memiliki file konfigurasi yang tepat yang melibatkan penulisan kode lebih banyak, tetapi menghindari IF sepenuhnya. - stamster
Google menyarankan untuk menggunakan 301 daripada 303. Sumber: support.google.com/webmasters/answer/6073543?hl=en - dylanh724
@DylanHunt - Saya hanya meninggalkan 303 untuk pengujian, mencatat bahwa 1st handler ditetapkan ke 301, hanya 2 saya lupa untuk mengubah :) Juga, solusi tanpa IF: stackoverflow.com/a/36777526/6076984 - stamster