Hapus "www" dan arahkan ke "https" dengan nginx

57

Saya ingin membuat aturan di nginx yang melakukan dua hal:

  1. Menghapus "www." dari permintaan URI
  2. Redirect ke "https" jika URI permintaan adalah "http"

Ada banyak contoh cara melakukan masing-masing hal secara individual, tetapi saya tidak dapat menemukan solusi yang melakukan keduanya dengan benar (yaitu tidak membuat redirect loop dan menangani semua case dengan benar).

Perlu menangani semua kasus ini:

1. http://www.example.com/path
2. https://www.example.com/path
3. http://example.com/path
4. https://example.com/path

Ini semua harus berakhir di https://example.com/path (# 4) tanpa perulangan. Ada ide?

Devin
sumber
Saya baru saja mengalihkan www.mydomain.com ke mydomain.com di tingkat DNS dan menambahkan 301 untuk non-https ke https di nginx. Sepertinya itu seharusnya baik-baik saja ¯ \ _ (ツ) _ / ¯
jonathanbell

Jawaban:

94

Cara terbaik untuk melakukannya adalah dengan menggunakan tiga blok server: satu untuk mengarahkan ulang http ke https, satu untuk mengarahkan ulang https www-name ke no-www, dan satu lagi untuk benar-benar menangani permintaan. Alasan untuk menggunakan blok server tambahan alih-alih seandainya pemilihan server dilakukan menggunakan tabel hash, dan sangat cepat. Menggunakan tingkat server jika berarti jika dijalankan untuk setiap permintaan, yang sia-sia. Juga, menangkap uri yang diminta dalam penulisan ulang itu sia-sia, karena nginx sudah memiliki informasi ini dalam variabel $ uri dan $ request_uri (tanpa dan dengan string kueri, masing-masing).

server {
    server_name www.example.com example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name example.com;

    <locations for processing requests>
}
kolbyjack
sumber
2
Apakah blok tengah diperlukan? Bukankah blok pertama sudah menulis ulang dari www ke non-www?
pbreitenbach
3
Blok pertama hanya menangani http. Blok tengah diperlukan untuk mengalihkan permintaan https dari https: // www.example.com/ ke https: // example.com/. (Maaf untuk ruang ekstra, saya tidak dapat membuatnya menampilkan https sebaliknya)
kolbyjack
1
hanya catatan pemformatan kecil - jika Anda ingin menghindari membuat tautan, Anda dapat memasukkan teks komentar di belakang tanda kutip `, yang di bawah tilde. Itu akan muncul seperti:https://example.com/
Cyclops
9
blok kedua juga membutuhkan info sertifikat.
ricka
3
Mencoba jawaban ini, saya mengalami masalah lain. Pikir saya bisa 301 redirect dari www.sub.example.comke sub.example.comdan kemudian hanya mendapatkan sertifikat SSL untuk sub.example.comSekarang saya tahu bahwa ssl cert memeriksa terjadi sebelum redirect 301, jadi tidak bisa bekerja. Penjelasan lebih lanjut di sini: serverfault.com/a/358625/144811
Gruzzles
11

Ini bekerja untuk saya:

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

server {
    listen              443 ssl;
    server_name         www.yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    return              301 https://yourdomain.com$request_uri;
}

server {
    listen              443 ssl;
    server_name         yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

    # do the proper handling of the request
}

Perlu diingat bahwa keduanya yourdomain.com dan www.yourdomain.com harus dalam sertifikat SSL Anda. Ini dimungkinkan dengan sertifikat wildcard atau dengan Server Alternate Name seperti yang dijelaskan di sini . Periksa https://www.startssl.com untuk sertifikat yang bagus dan gratis yang melakukan ini. ( Edith : dimulai dengan Chrome versi 56, sertifikat beginsl tidak akan dipercaya lagi. Coba https://letsencrypt.org/ sebagai gantinya.)

e18r
sumber
Yang ini benar-benar berfungsi, tapi saya pikir itu bisa dilakukan dengan cara yang lebih jelas tanpa banyak baris konfigurasi duplikat.
zloynemec
@ zloynemec Anda bisa meletakkan barang-barang SSL dalam file .conf yang terpisah dan menggunakan includeaturan untuk menambahkannya ke kedua blok server SSL.
Igettäjä
Juga jika Anda menggunakan cloudflare, Anda harus membayar $ 10 / bulan untuk dapat mengarahkan dan mem-proxy 2 subdomain (www + sesuatu). Beritahu saya jika ada solusi.
Freedo
7

Setelah menghabiskan begitu banyak waktu dengan ratusan kasus serupa, saya telah membuat potongan berikut. Ini pendek dan dapat dengan mudah diubah agar sesuai dengan apa pun.

server {
    listen 80;
    listen 443 ssl;
    server_name example.com www.example.com;
    ssl_certificate /path/to/my/certs/example.com/fullchain.pem;
    ssl_certificate_key /path/to/my/certs/example.com/privkey.pem;

    # Redirect to the correct place, if needed
    set $https_redirect 0;
    if ($server_port = 80) { set $https_redirect 1; }
    if ($host ~ '^www\.') { set $https_redirect 1; }
    if ($https_redirect = 1) {
        return 301 https://example.com$request_uri;
    }

    location / {
    # ...
}

Oh, tapi ifitu jahat !

Ya bisa . Tapi itu ada karena suatu alasan, dan seharusnya tidak membahayakan mereka yang tahu cara menggunakannya dengan benar. ;)

Emyller
sumber
Saya suka ini, tetapi apakah Anda memiliki data tentang hit kinerja? Terima kasih!
Freedo
1
Jujur saya tidak pernah membandingkannya, tapi saya percaya hampir tidak akan ada dampak dibandingkan dengan aturan yang terpisah karena efeknya hampir sama.
emyller
patokan pada pengalihan? itu tidak benar-benar relevan bukan? (pertanyaan sebenarnya, bukan troll ^^)
Matrix
3

Saya lebih suka kembali dengan kode respons sehingga browser tahu Anda mengarahkannya ke URL lain.

server {
    listen   80;
    server_name  www.example.com;

    return 301 https://example.com$request_uri;
}

kemudian konfigurasi server lain memblokir untuk https

server {
        listen   443 ssl;
        server_name  example.com;
        ...
    }
montss
sumber
0

bagaimana cara membuat blok server untuk tujuan ini:

server{
    listen 80;
    server_name www.example.net example.net;
    rewrite ^(.*) https://example.net$1 permanent;
}

kemudian restart nginx

anthonysomerset
sumber
Saya mendapatkan kesalahan "nama server yang bentrok" saat memulai ulang. Juga, perintah yang tidak akan mendengarkan pada port 443 untuk SSL dan saya perlu khawatir tentang mengarahkan https://www.example.comuntuk https://example.comjuga.
Devin
0

Saya pikir ini harus berhasil.

Pada definisi server HTTP polos Anda sesuatu seperti anthonysomerset disarankan, yaitu:

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

Kemudian pada definisi server SSL Anda:

if ($host ~ /^www\./) {
  rewrite ^(.*) https://example.net$1 permanent;
}

Dengan cara ini pengalihan hanya akan terjadi sekali per permintaan, tidak peduli URL mana yang digunakan pengguna semula.

Eduardo Ivanec
sumber
Itu berhasil, terima kasih. Saya harus mengubah persyaratan if ($host = 'www.example.com') {Anda karena regex Anda tidak bekerja untuk saya. Tidak tahu mengapa, karena terlihat benar.
Devin
Perhatikan bahwa jika itu jahat dan umumnya lebih baik menggunakan cara deklaratif.
Blaise
0

Inilah contoh lengkap yang akhirnya berhasil untuk saya. Masalahnya adalah bahwa saya tidak memiliki rincian ssl ( ssl_certificate, dll.) Di blok redirect www. Ingatlah untuk memeriksa log Anda ( sudo tail -f /var/log/nginx/error.log)!

# HTTP — redirect all traffic to HTTPS
server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}

# HTTPS — redirects www to non-www
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;
    return 301 https://example.com$request_uri;
}

# HTTPS — proxy all requests to the app (port 3001)
server {
    # Enable HTTP/2
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com sub.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;

    # For LetsEncrypt:
    location ~ /.well-known {
        root /var/www/html;
        allow all;
    }

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:3001;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
}

sumber