ฉันต้องการเขียนคำขอ http ทั้งหมดบนเว็บเซิร์ฟเวอร์ของฉันใหม่เป็นคำขอ https ฉันเริ่มต้นด้วยสิ่งต่อไปนี้:

server {
    listen      80;

    location / {
      rewrite     ^(.*)   https://mysite.com$1 permanent;
    }
...


ปัญหาหนึ่งคือสิ่งนี้จะลบข้อมูลโดเมนย่อยทั้งหมด (เช่น node1.mysite.com/folder) ฉันจะเขียนข้อความข้างต้นใหม่เพื่อเปลี่ยนเส้นทางทุกอย่างไปที่ https และรักษาโดเมนย่อยได้อย่างไร

answer

วิธีที่ถูกต้องใน nginx . เวอร์ชันใหม่

ปรากฏว่าคำตอบแรกของฉันสำหรับคำถามนี้ถูกต้องในบางช่วงเวลา แต่กลายเป็นข้อผิดพลาดอีกอย่างหนึ่ง - หากต้องการติดตามข่าวสารล่าสุด โปรดตรวจสอบข้อผิดพลาดในการเขียนซ้ำของ Taxing

ฉันได้รับการแก้ไขโดยผู้ใช้ 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; 

       [....]
}

หมายเหตุ: วิธีที่ดีที่สุดในการทำเช่นนี้คือ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;
 }

ส่งการเปลี่ยนเส้นทางถาวรไปยังไคลเอนต์

ฉันคิดว่าวิธีที่ดีที่สุดและเป็นวิธีเดียวที่ควรใช้HTTP 301 Moved Permanentlyเปลี่ยนเส้นทางดังนี้:

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

HTTP 301 ย้ายถาวรเปลี่ยนเส้นทางยังมีประสิทธิภาพมากที่สุดเพราะมี regex ที่จะได้รับการประเมินไม่เป็นไปตามที่กล่าวแล้ว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 หรือต้องการผูกที่อื่น

วิธีแก้ปัญหาจาก Saif Bechan ใช้server_nameในกรณีของฉันคือ localhost แต่ไม่สามารถเข้าถึงได้ผ่านเครือข่าย

วิธีแก้ปัญหาจาก Michael Neale นั้นดี แต่จากข้อผิดพลาด มีวิธีแก้ปัญหาที่ดีกว่าด้วยการเปลี่ยนเส้นทาง 301 ;)

ภายในบล็อกเซิร์ฟเวอร์ คุณสามารถทำสิ่งต่อไปนี้ได้:

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

ด้านบนใช้ไม่ได้กับโดเมนย่อยใหม่ที่สร้างขึ้นตลอดเวลา เช่น 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
# ...
}

ฉันโพสต์ความคิดเห็นเกี่ยวกับคำตอบที่ถูกต้องเมื่อนานมาแล้วโดยมีการแก้ไขที่สำคัญมาก แต่ฉันรู้สึกว่าจำเป็นต้องเน้นการแก้ไขนี้ในคำตอบของตัวเอง ไม่มีคำตอบใดๆ ก่อนหน้านี้ปลอดภัยที่จะใช้หากคุณมีการตั้งค่า HTTP ที่ไม่ปลอดภัยและคาดหวังเนื้อหาของผู้ใช้ มีแบบฟอร์ม โฮสต์ API หรือกำหนดค่าเว็บไซต์ เครื่องมือ แอปพลิเคชัน หรือยูทิลิตี้ใดๆ เพื่อพูดคุยกับไซต์ของคุณ

ปัญหาเกิดขึ้นเมื่อมีการ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"; 

       [....]
}

ฉันทำได้ดังนี้:

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

ฉันกำลังใช้งาน ngnix หลัง AWS ELB ELB กำลังคุยกับ ngnix ผ่าน http เนื่องจาก ELB ไม่มีวิธีส่งการเปลี่ยนเส้นทางไปยังไคลเอนต์ ฉันจึงตรวจสอบส่วนหัว X-Forwarded-Proto และเปลี่ยนเส้นทาง:

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

หากคุณreturn 301 https://$host$request_uri;เป็นการตอบสนองเริ่มต้นในพอร์ต 80 เซิร์ฟเวอร์ของคุณอาจได้รับรายชื่อพร็อกซีที่เปิดอยู่ไม่ช้าก็เร็ว[1] และเริ่มถูกใช้ในทางที่ผิดเพื่อส่งทราฟฟิกไปที่อื่นบนอินเทอร์เน็ต หากบันทึกของคุณเต็มไปด้วยข้อความเช่นนี้ แสดงว่าคุณรู้ว่าสิ่งนี้เกิดขึ้นกับคุณ:

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

ปัญหาคือ$hostจะสะท้อนกลับสิ่งที่เบราว์เซอร์ส่งในHostส่วนหัวหรือแม้แต่ชื่อโฮสต์จากบรรทัดเปิดของ HTTP เช่นนี้:

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

เนื่องจากปัญหาดังกล่าว คำตอบอื่นๆ ที่นี่จึงแนะนำให้ใช้$server_nameแทน$host. $server_nameประเมินสิ่งที่คุณใส่ในการserver_nameประกาศเสมอ แต่ถ้าคุณมีโดเมนย่อยหลายโดเมนหรือใช้ไวด์การ์ด สิ่งนั้นจะไม่ทำงาน เพราะ$server_nameใช้เฉพาะรายการแรกหลังจากการserver_nameประกาศ และที่สำคัญกว่านั้นก็จะสะท้อนกลับไวด์การ์ด (ไม่ขยาย)

แล้วจะสนับสนุนหลายโดเมนในขณะที่รักษาความปลอดภัยได้อย่างไร? ในระบบของฉันเองฉันได้กระทำกับภาวะที่กลืนไม่เข้าคายไม่ออกนี้โดยครั้งแรกรายชื่อdefault_serverบล็อกที่ไม่ได้ใช้$hostแล้วรายชื่อบล็อกสัญลักษณ์ตัวแทนที่ไม่:

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

(คุณสามารถระบุโดเมนได้มากกว่าหนึ่งโดเมนในบล็อกที่สอง)

ด้วยการผสมผสานดังกล่าว โดเมนที่ไม่ตรงกันจะถูกเปลี่ยนเส้นทางไปยังที่ใดที่หนึ่งฮาร์ดโค้ด (เสมอexample.com) และโดเมนที่ตรงกับโดเมนของคุณจะถูกนำไปที่ที่ถูกต้อง เซิร์ฟเวอร์ของคุณจะไม่มีประโยชน์ในฐานะพร็อกซีแบบเปิด ดังนั้นคุณจะไม่ต้องประสบปัญหา

หากคุณรู้สึกแย่ ฉันคิดว่าคุณสามารถทำให้การdefault_serverบล็อกไม่ตรงกับโดเมนที่ถูกต้องตามกฎหมายของคุณและให้บริการบางอย่างที่ไม่เหมาะสม . . .

[1] ในทางเทคนิคคำว่า "พร็อกซี" เป็นคำที่ไม่ถูกต้อง เนื่องจากเซิร์ฟเวอร์ของคุณไม่ได้ออกไปทำงานและดำเนินการตามคำขอของลูกค้า เพียงส่งการเปลี่ยนเส้นทาง แต่ฉันไม่แน่ใจว่าคำที่เหมาะสมคืออะไร ฉันเองก็ไม่แน่ใจเหมือนกันว่าเป้าหมายคืออะไร แต่มันทำให้บันทึกของคุณเต็มไปด้วยเสียงรบกวน และใช้ CPU และแบนด์วิดท์ของคุณ ดังนั้นคุณจึงควรหยุดมันเสีย

ดูเหมือนไม่มีใครเข้าใจถูก 100% หากต้องการให้คำขอพอร์ต 80 ไปที่ 443 ที่เทียบเท่ากันสำหรับเว็บเซิร์ฟเวอร์ทั้งหมดคุณต้องใช้คำสั่งlistenไม่ใช่คำสั่ง server_name เพื่อระบุชื่อที่รับทั้งหมด ดูเพิ่มเติมที่https://nginx.org/en/docs/http/request_processing.html

server {
    listen 80 default;
    listen [::]:80 default;
      return 307 https://$host$request_uri;
}
  • $host จับชื่อโดเมนย่อย
  • 307 และ 308 มีทั้ง URI คำขอ POST และ GET
  • 307 เป็นแบบชั่วคราว เปลี่ยนเป็นแบบถาวร 308 หลังจากการทดสอบอย่างละเอียด:

และตรวจสอบให้แน่ใจว่าคุณได้ตรวจสอบสิ่งที่มีอยู่แล้วใน /etc/nginx/conf.d/ เพราะบ่อยครั้งที่ฉันมีปัญหาที่ default.conf ส่งคืน vhost ที่มีอยู่ ลำดับการทำงานของฉันกับปัญหา nginx มักจะเริ่มต้นโดยการย้ายไฟล์เริ่มต้นออก นำกลับมาแสดงความคิดเห็นทีละบรรทัดเพื่อดูว่ามันผิดพลาดตรงไหน

rewrite ^!https https://$host$request_uri permanent;