سؤال في Nginx ، كيف يمكنني إعادة كتابة جميع طلبات http إلى https مع الحفاظ على النطاق الفرعي؟


أريد إعادة كتابة جميع طلبات http على خادم الويب الخاص بي لتكون طلبات https ، فقد بدأت بالآتي:

الخادم {
    الاستماع 80 ؛

    موقعك / {
      أعد كتابة ^ (. *) https: //mysite.com$1 دائمة؛
    }
...


هناك مشكلة واحدة وهي أن هذا يزيل أي معلومات عن النطاق الفرعي (على سبيل المثال ، node1.mysite.com/folder) ، فكيف أعيد كتابة ما سبق لإعادة توجيه كل شيء إلى https والحفاظ على النطاق الفرعي؟


495
2017-09-21 14:04


الأصل


يرجى النظر في نقل "الإجابة المقبولة" على serverfault.com/a/171238/90758. هذا هو الصحيح. - olafure
مجرد استخدام $ server_name بدلا من myside.com hardcoded - Fedir RYKHTIK


الأجوبة:


الطريقة الصحيحة في الإصدارات الجديدة من nginx

تبين أن إجابتي الأولى على هذا السؤال كانت صحيحة في وقت معين ، ولكنها تحولت إلى فوضى أخرى - لكي تبقى على اطلاع حتى الآن ، يرجى التحقق من ذلك فرض الضرائب إعادة المزالق

لقد تم تصحيحي من قبل العديد من مستخدمي SE ، لذا فإن الفضل يعود لهم ، ولكن الأهم من ذلك ، هنا هو الرمز الصحيح:

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



قد تضطر إلى القيام بذلك على نطاق بواسطة أساس المجال ومع - لا؟ ماذا لو كنت تريد تطبيقه على كل نطاق على خادمك؟ - JM4
@ JM4: إذا كنت تستخدم $ host $ في إعادة الكتابة بدلاً من server_name وأضف default_server إلى توجيه الاستماع ، فستعمل مع كل نطاق على خادمك. - Klaas van Schelven
من المهم الإشارة إلى أنه تم تخزين 301 في ذاكرة التخزين المؤقت المحلية الخاصة بك دون تاريخ انتهاء الصلاحية. ليست مفيدة جدا عندما يتغير التكوين - Trefex
everyone استخدم إعادة توجيه 307 للحفاظ على محتوى POST. - Mahmoud Al-Qudsi
لاحظ أنه يجب عليك استخدام $host بدلا من $server_name إذا كنت تستخدم النطاقات الفرعية. - Catfish


ملاحظة: تم توفير أفضل طريقة للقيام بذلك بواسطة https://serverfault.com/a/401632/3641 - ولكن يتكرر هنا:

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

في أبسط الحالات ، سيتم إصلاح المضيف ليصبح خدمتك التي تريد إرسالها إليها - سيؤدي ذلك إلى إعادة التوجيه 301 إلى المتصفح وسيتم تحديث عنوان URL للمتصفح وفقًا لذلك.

في ما يلي الإجابة السابقة ، وهي غير فعالة بسبب regex ، وهي بسيطة 301 رائعة كما هو موضح من قبلkmindi

لقد استخدمت nginx 0.8.39 وما فوق ، واستخدمت ما يلي:

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

يرسل إعادة توجيه دائمة إلى العميل.


270
2017-08-17 03:07



أعتقد أنه يجب أن يكون 80 - لأن هذا هو الاستماع ل http ومن ثم إخبار العميل بالعودة إلى https (443). - Michael Neale
هذا يجب أن يكون الجواب الأعلى! - Nathan
هذه هي الإجابة الأكثر ضرائب. - Case
هذا هو الأسهل ، ولكن الأقل أمانًا - بهذه الطريقة تسمح لخادمك بإعادة توجيه المستخدم إلى أي صفحة ، دون التحقق مما إذا كان مسموحًا حتى باستخدامه على خادمك. إذا كان الخادم يخدم mydomain.co ، فلا يزال بإمكان المستخدمين الضارين استخدام خادمك لإعادة توجيه المستخدمين إلى نطاقات أخرى مثل mydomain.co ، مثل google.com. - friedkiwi
@ cab0lt لا توجد مشكلة أمنية هنا. لا يمثل تقديم إعادة توجيه خطرًا أمنيًا. إذا كانت هناك متطلبات للتحكم في الوصول ، فيجب التحقق منها عند النقطة التي يطلب المتصفح فيها عنوان URL الجديد. لن يتمكن المتصفح من الدخول ببساطة على أساس عملية إعادة التوجيه ، ولا يحتاج إلى إعادة التوجيه لطلب عنوان URL الجديد. - mc0e


أعتقد أن الطريقة الأفضل والوحيدة يجب أن تستخدم a HTTP 301 Moved Permanently إعادة توجيه مثل هذا:

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

ال HTTP 301 Moved Permanently كما أن إعادة التوجيه هي الأكثر فاعلية لعدم وجود تعبير منطقي يتم تقييمه ، وفقًا لما سبق ذكره 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، لذلك يرتبط أيضا إلى مقبس ipv4. تغييره الى listen 80 إذا كنت لا تريد IPv6 أو تريد الربط في مكان آخر.

الحل من سيف Bechan يستخدم server_name والتي في حالتي هي مضيف محلي ولكن هذا لا يمكن الوصول إليه عبر شبكة.

الحل من مايكل نيل جيد ، ولكن وفقا ل pitfails ، هناك حل أفضل مع إعادة توجيه 301 ؛)


121
2018-06-23 17:19



لطيفة تحاول اقتباسها ، لكن 301 لا يعمل على HTTPS. - Case
ما لا يعمل؟ قسم الخادم المذكور هو لحركة مرور http (بدون s) غير المشفرة ليتم إعادة توجيهها بشكل دائم إلى خادم مشفر (ذلك القسم الذي يستمع إلى 443 (https) غير مدرج) - kmindi
راجعت هذا يعمل بشكل رائع مع https وكل شيء -kmindi قمت بتحديث إجابتي بالإشارة إلى ملكيتك - كما أعتقد أنها الطريقة الصحيحة ، وهذا يستمر في الظهور! عمل جيد. - Michael Neale
عند استخدام طلب المجال (غير ip) ، لا يعمل إلا إذا قمت بتغيير "[::]: 80" إلى "80". - Joseph Lust
يمكن أن يكون السلوك المتوقع: trac.nginx.org/nginx/ticket/345. لقد قمت بتحديث الإجابة لوصف خيار الاستماع. - kmindi


لم يعمل ما سبق مع مع إنشاء نطاقات فرعية جديدة طوال الوقت. مثلا 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
# ...
}

17
2018-06-25 04:29



شكرا جزيلا! nginx إما العودة 301 https://*/ أو إلغاء الطلب قبل الأوان في الإجابات الأخرى هنا. server_name _; مع $host كان الجواب الذي فعل الخدعة. +1 - zamnuts
هذا واحد هو الأمثل! ومع ذلك ، فإنني أوصي لبعض ليحل محل _ مع المجال الفعلي ، على سبيل المثال .domain.com كان لدي اثنين من الخوادم ، وكان nginx توجيه أحد الخوادم الخاصة بي عن طريق الخطأ إلى الخادم الافتراضي. - zzz
هذه هي الإجابة الوحيدة التي عملت بالنسبة لي ، شكرا! - Snowman
شكرا جزيلا الأصدقاء ... لقد جربت العديد من الحلول ولكن لم يعمل. هذه الحلول رائعة وعملت بالنسبة لي. اسم الخادم _؛ ما المقصود بهذا .. لم أفهم. من فضلك اشرح لي هذا. - Pavan Kumar


ضمن كتلة الخادم ، يمكنك أيضًا القيام بما يلي:

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

16
2017-07-31 19:50



تسبب هذا التكوين الخادم الخاص بي لإنتاج حلقة إعادة توجيه - Corkscreewe
ربما يتسبب في وجود عملية إعادة توجيه أخرى في المكان أو أن https غير ممكّن في موقعك / تطبيقك - Oriol
يبدو أن أيا من الآخرين لا يعمل إلا هذا واحد. باستخدام إصدار nginx: nginx / 1.10.0 (Ubuntu) - ThatGuy343
upvoted لـ https: // $ host $ uri - AMB
هذا هو الطريق للذهاب إذا كنت وراء loadbalancer! - Antwan


نشرت تعليقًا على الإجابة الصحيحة منذ فترة طويلة من الزمن مع تصحيح مهم جدًا ، لكنني أشعر أنه من الضروري إبراز هذا التصحيح في جوابه الخاص. لا تعد أي من الإجابات السابقة آمنة للاستخدام إذا كان لديك في أي وقت إعداد HTTP غير آمن ونتوقع محتوى مستخدم ، أو لديك نماذج ، أو تستضيف واجهة برمجة تطبيقات ، أو قمت بتكوين أي موقع ويب ، أو أداة ، أو تطبيق ، أو أداة مساعدة للتحدث إلى موقعك.

المشكلة تحدث عندما 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"; 

       [....]
}

6
2018-03-19 20:24



أمر مفيد جدا جدا جدا. شكر! - Denis Matafonov


تمكنت من القيام بذلك على النحو التالي:

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



الإصدار المحدّث ، w / o IF: paste.debian.net/plain/899679 - stamster