Nginx'te alt etki alanını korurken tüm http isteklerini https'ye nasıl yeniden yazabilirim?


508

Web sunucumdaki tüm http isteklerini https istekleri olarak yeniden yazmak istiyorum, aşağıdakilerle başladım:

sunucu {
    80 dinle;

    yer / {
      yeniden yazmak ^ (. *) https: //mysite.com$1 kalıcı;
    }
...


Bir Sorun, bunun herhangi bir alt alan bilgisini (örneğin, node1.mysite.com/folder) ortadan kaldırmasıdır, her şeyi https'ye yönlendirmek ve alt alanı korumak için yukarıdakileri nasıl yeniden yazabilirim?


2
Lütfen 'kabul edilen yanıtı' serverfault.com/a/171238/90758 adresine taşımayı düşünün . Doğru olan bu.
olafure

Yanıtlar:


748

Nginx'in yeni sürümlerinde doğru yol

Bu soruya verdiğim ilk cevabı belli bir zamanda doğruydu, ancak başka bir tuzağa düştü - güncel kalmak için lütfen Vergilendirme tekrar yazma riskini kontrol edin

Birçok SE kullanıcısı tarafından düzeltildim, bu yüzden kredi onlara doğru gidiyor, ancak daha önemlisi, burada doğru kod:

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
Ancak bunu bir etki alanında etki alanı temelinde yapmanız gerekir - hayır? Ya sunucunuzdaki her etki alanına uygulamak istiyorsanız?
JM4

30
@ JM4: Eğer rnrite server_name yerine $ host $ kullanıyorsanız ve default_server 'ı listen yönergesine eklerseniz, sunucunuzdaki her alan için işe yarayacaktır.
Klaas van Schelven

5
301'in son kullanma tarihi olmayan yerel önbellekte saklandığını belirtmek önemlidir. Config değiştiğinde çok kullanışlı değil
Trefex

9
@everyone POST içeriğini korumak için 307 yönlendirmesini kullanın.
Mahmud Al-Qudsi,

10
Alt etki alanları kullanıyorsanız kullanmak $hostyerine kullanmanız gerektiğini unutmayın $server_name.
Yayın balığı

276

NOT: Bunu yapmanın en iyi yolu https://serverfault.com/a/401632/3641 tarafından sağlanmıştır - ancak burada tekrarlanır:

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

En basit durumda, sunucunuzun size göndermek istediğiniz hizmetiniz olması düzeltilecektir - bu, tarayıcıya bir 301 yönlendirmesi yapar ve tarayıcı URL'si buna göre güncellenir.

Aşağıda regex nedeniyle yetersiz olan önceki cevap, basit 301 @kmindi tarafından gösterildiği gibi harika

Nginx 0.8.39 ve üstünü kullanıyorum ve aşağıdakileri kullandım:

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

İstemciye kalıcı bir yönlendirme gönderir.


15
Bence 80 olmalı - bu http dinliyor ve ardından müşteriye https (443) olarak geri dönmesini söylüyor.
Michael Neale,

3
Bu en iyi cevap olmalı!
Nathan,

3
Bu en vergi cevabı.
Dava

1
bu en kolay, ancak en az güvenli olandır - bu sayede sunucunuzun, sunucunuzda kullanılmasına izin verilip verilmediğini kontrol etmeden bir kullanıcıyı herhangi bir sayfaya yönlendirmesini sağlarsınız. Sunucunuz etkialanim.com sitesine hizmet veriyorsa, kötü amaçlı kullanıcılar, sunucunuzu yine de google.com gibi etkialanim alanlarım gibi diğer etki alanlarına yönlendirmek için kullanabilir.
friedkiwi

10
@ cab0lt burada güvenlik sorunu yoktur. Yeniden yönlendirme yapmak güvenlik riski oluşturmaz. Erişim kontrolü gereksinimleri varsa, bunlar tarayıcının yeni URL'yi istediği noktada kontrol edilmelidir. Tarayıcı, yalnızca yönlendirme temelinde erişemez ve yeni URL'yi istemek için yönlendirmeye de ihtiyaçları olmaz.
mc0e

125

Bence en iyi ve tek yol, bir HTTP 301 Hareketli Kalıcı olarak şu şekilde yönlendirmeyi kullanmak olmalıdır :

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

Kalıcı taşındı HTTP 301 Daha önce de belirtildiği göre, değerlendirilecek herhangi bir normal ifade yoktur, çünkü yönlendirme da en verimli pitfails .


Yeni HTTP 308 Taşındı, Sürekli olarak İstek yöntemini koruyor ve büyük tarayıcılar tarafından destekleniyor . Örneğin, kullanılarak 308önler gelen istek yöntemi değiştirmesini tarayıcıları POSTiçin GETyönlendirme talebi için.


Ana bilgisayar adını ve alt etki alanını korumak istiyorsanız bu yol budur.

Bu , hala yerel olarak kullandığım için DNS’iniz yoksa da işe yarıyor . Örneğin ile istiyorum http://192.168.0.100/index.phpve tam olarak yönlendirileceksiniz https://192.168.0.100/index.php.

Kullandığım listen [::]:80ben çünkü benim ana bilgisayarda bindv6onlyayarlı false, bu nedenle de ipv4 soketine bağlanır. olarak değiştirin listen 80Eğer IPv6 istemiyorsanız veya başka bir yerde bağlamak istiyorum.

Saif Bechan'ın çözümü server_namebenim durumumda yerel ev sahibi olanı kullanıyor ancak bu ağ üzerinden erişilemiyor.

Michael Neale’in çözümü iyidir, ancak tuzaklara göre, yönlendirme 301’le daha iyi bir çözüm vardır;)


Güzel, bunu alıntılamaya çalışırsanız, ancak 301 HTTPS'de çalışmaz.
Dava

5
ne çalışmıyor? belirtilen sunucu bölümü şifrelenmemiş http (ler olmadan) trafiğinin kalıcı olarak şifrelenmiş sunucuya yönlendirilmesi içindir (443'te (https) dinleyen bölüm listelenmemiş)
kmindi

Bunun https ve her şeyle mükemmel çalıştığını kontrol ettim. İyi iş.
Michael Neale,

Bir etki alanı (ip olmayan) isteği kullanırken, '[::]: 80' ila '80' değerini değiştirmediğim sürece çalışmaz.
Joseph Lust

Beklenen davranış bu olabilir: trac.nginx.org/nginx/ticket/345 . Dinleme seçeneğini tanımlamak için cevabı güncelledim.
20'de

20

Sunucu bloğu içinde ayrıca şunları da yapabilirsiniz:

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

2
Bu yapılandırma sunucumun bir yönlendirme döngüsü üretmesine neden oldu
Corkscreewe

Belki de yerinde başka bir yönlendirme var ya da sitenizde / uygulamanızda https etkinleştirilmemişse
Oriol

1
Bunlardan başka hiçbiri çalışmıyor gibiydi. Nginx sürümünü kullanma: nginx / 1.10.0 (Ubuntu)
ThatGuy343

https: // $ host $ uri için oy verildi
AMB

4
Eğer bir loadbalancerın arkasındaysan, gitmenin yolu budur!
Antwan

17

Yukarıdakiler her zaman yeni alt alanlar oluşturulmuş olarak işe yaramadı. örneğin yaklaşık 30 alt alan için AAA.example.com BBB.example.com.

Sonunda aşağıdakilerle çalışan bir yapılandırma var:

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

teşekkür ederim! nginx 301 https://*/, buradaki diğer cevaplarda vaktinden önce isteği geri ya da iptal eder. server_name _;ile $hosthile yapan cevap oldu. +1
zamn

1
Bu en uygun! Ancak, bazılarının _asıl etki alanıyla değiştirilmesini öneririm , örneğin .domain.comiki sunucum vardı ve nginx yanlışlıkla sunucularımdan birini varsayılan sunucuya yönlendiriyordu.
zzz

1
Bu benim için çalışan tek cevap, teşekkürler!
Kardan

Çok teşekkürler dostum .. Çözümlerin çoğunu denedim ama işe yaramadı. Bu çözümler harika ve benim için çalıştı. sunucu adı _; bunun ne anlama geldiğini .. anlamadım. Lütfen bana bunu açıkla.
Pavan Kumar

6

Çok uzun bir süre önce çok önemli bir düzeltme ile doğru cevap hakkında bir yorum yaptım, ancak bu düzeltmeyi kendi cevabında vurgulamanın gerekli olduğunu düşünüyorum. Güvenli olmayan bir HTTP kurulumu yapmış ve kullanıcı içeriği beklemiş, formlar yapmış, bir API barındırmış veya sitenizle konuşmak için herhangi bir web sitesi, araç, uygulama veya yardımcı program yapılandırmış olsaydınız, önceki yanıtların hiçbiri güvenli değildir.

POSTSunucunuz için bir istek yapıldığında sorun oluşur . Sunucu düz bir 30xyönlendirme ile yanıt verirse POST içeriği kaybolur. Ne olur tarayıcı / istemci SSL isteği yükseltme ama olmasıdır eski sürümePOST a GETisteği. POSTParametreler kaybolacak ve yanlış istek sunucuya yapılacaktır.

Çözüm basit. Bir HTTP 1.1 307yönlendirme kullanmanız gerekir . Bu, RFC 7231 S6.4.7'de ayrıntılı olarak açıklanmıştır:

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

Kabul edilen çözümden uyarlanan çözüm, 307yönlendirme kodunuzda kullanmak içindir:

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

Böyle yapmayı başardım:

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


Güncelleme sürümü, IF olmadan
stamster

4

Bir AWS ELB'in arkasında ngnix kullanıyorum. ELB, http üzerinden ngnix ile konuşuyor. ELB'nin istemcilere yönlendirme gönderme yolu olmadığından, X-Forward-Proto üstbilgisini denetler ve yönlendiririm:

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

1

Eğer varsa return 301 https://$host$request_uri;80 numaralı bağlantı noktasında varsayılan yanıt olarak, o zaman sunucu er ya da geç [1] açık vekiller listesi binip başlayabilir internette başka bir yerde trafik göndermek için istismar ediliyor. Günlüklerin bunun gibi mesajlarla doluysa, o zaman senin başına geldiğini biliyorsun:

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

Sorun şu ki $host, tarayıcının Hostbaşlıkta ne gönderirse gönderilsin, hatta ana bilgisayar adını HTTP'nin açılış satırından geri gönderir, bunun gibi:

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

Bu sorun nedeniyle, burada bazı diğer cevaplar $server_nameyerine kullanmanızı öneririz $host. $server_namehep ne değerlendirir sen koymak server_namebeyanı. Ancak, orada birden fazla alt etki alanınız varsa veya bir joker karakter kullanıyorsanız, bu işe yaramaz, çünkü $server_nameyalnızca bildirimden sonraki ilk girişi kullanır server_nameve daha da önemlisi bir joker karakteri geri döndürür (genişletmez).

Peki, güvenliği sürdürürken birden fazla alanı nasıl destekleyebilirim? Kendi sistemlerimde, bu ikilemle önce , default_serverkullanmayan bir bloğu listeleyip $host, ardından aşağıdakileri yapan bir joker karakter bloğu listeleyerek ele aldım :

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

(İkinci blokta birden fazla etki alanı da listeleyebilirsiniz.)

Bu kombinasyonla, example.comeşleştirilmemiş alanlar kodlanmış (herhangi bir zamanda) kodlanmış bir yere yönlendirilir ve kendinize uygun alanlar doğru yere gider. Sunucunuz açık bir proxy olarak kullanılamaz, bu nedenle sorun çıkarmazsınız.

Eğer kaba hissediyorsanız, ben de yapabilir varsayalım default_serverblok maç hiçbiri sizin meşru alanların ve bir şey saldırısını hizmet vermektedir. . . .

[1] Teknik olarak "proxy" yanlış kelime çünkü sunucunuz dışarı çıkmıyor ve istemciler için istekleri yerine getirmiyor, sadece bir yönlendirme gönderiyor, ancak doğru kelimenin ne olacağından emin değilim. Ayrıca hedefin ne olduğundan da emin değilim, ancak günlüklerinizi gürültü ile dolduruyor ve CPU'nuzu ve bant genişliğinizi tüketiyor, bu yüzden buna bir son verebilirsiniz.


0

Hiç kimse gerçekten% 100 doğru anlamadı. 80 numaralı bağlantı noktasının tüm web sunucusu için 443 eşdeğerine gitmesini sağlamak için, hepsini yakalama adını belirtmek için server_name yönergesini değil, dinleme yönergesini kullanmanız gerekir . Ayrıca bkz. Https://nginx.org/en/docs/http/request_processing.html

sunucu {
    80 varsayılan dinle;
    [[]] dinle: 80 varsayılan;
      dönüş 307 https: // $ host $ request_uri;
}
  • $ host alt alan adlarını yakalar.
  • 307 ve 308, hem POST hem de GET istek URI'lerini içerir.
  • 307 Geçici, kapsamlı bir testten sonra Kalıcı 308 olarak değiştirin:

Ve zaten /etc/nginx/conf.d/ adresinde olanları kontrol ettiğinizden emin olun, çünkü daha sık olarak default.conf dosyasının varolan bazı vhost'ları döndürdüğü sorunlar yoktu. Nginx sorunları ile çalışma sırası her zaman varsayılan dosyanın yerini değiştirerek başlıyor, nerede yanlış gittiğini görmek için satır satır tekrar yorum yapıyor.


-1
rewrite ^!https https://$host$request_uri permanent;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.