วิธีลบพา ธ ด้วย nginx proxy_pass


77

ฉันมีการเรียกใช้เว็บแอพลิเคชันที่http://example.com/และต้องการที่จะ "ติด" http://example.com/enโปรแกรมประยุกต์อื่นบนเซิร์ฟเวอร์ที่แยก เซิร์ฟเวอร์อัปสตรีมและproxy_passดูเหมือนจะใช้งานได้ แต่มีปัญหาเดียว:

upstream luscious {
 server lixxxx.members.linode.com:9001;
}

server {
  root /var/www/example.com/current/public/;
  server_name example.com;

  location /en {
    proxy_pass http://luscious;
  }
}

เมื่อเปิดexample.com/enแอปพลิเคชันอัปสตรีมของฉันจะกลับ404 not found /enมา /enนี้ทำให้รู้สึกเป็นต้นน้ำไม่ได้มีเส้นทาง

เป็นproxy_pathทางออกที่เหมาะสม? ฉันควรเขียนใหม่ "upstream" เพื่อฟัง/enแทนหรือไม่เนื่องจากเป็นเส้นทางของรูท หรือมีคำสั่งที่อนุญาตให้ฉันเขียนเส้นทางที่ส่งผ่านไปยังต้นน้ำหรือไม่

คำตอบ:


133

นี่เป็นวิธีที่มีประสิทธิภาพที่สุดในการทำสิ่งที่คุณต้องการโดยไม่ต้องใช้นิพจน์ทั่วไป:

location = /en {
    return 302 /en/;
}
location /en/ {
    proxy_pass http://luscious/;  # note the trailing slash here, it matters!
}

1
เท่าที่ฉันรู้ส่วนสุดท้ายจะยังคงผ่าน "/ en" เป็นเส้นทางตามพร็อกซีใช่มั้ย
berkes

15
@berkes, ไม่, มันจะไม่ - การเฉือนต่อท้ายproxy_passเป็นสิ่งที่สร้างความแตกต่าง นอกจากนี้คำตอบนี้ถูกต้องมากกว่าคำตอบของคุณเพราะมันช่วยให้มั่นใจได้ว่าproxy_redirectจะอยู่ที่defaultดังนั้นคุณยังสามารถใช้302et al ภายในแบ็กเอนด์ของคุณและทำให้มันทำงานได้อย่างถูกต้องทุกที่
cnst

4
อ่าฉันพลาดเครื่องหมายท้าย: (
Vanuan

3
AAARGH! แทงอย่างแรง!
barrymac

4
3 ชั่วโมงในการค้นหาและใช่ ... มันเป็นเครื่องหมายทับ ขอบคุณเพื่อน!
ลูคัสพี.

12

ฉันต้องการพูดถึงคำตอบที่ใหม่กว่าซึ่งเป็นที่นิยมมากขึ้น

location ~ ^/en(/?)(.*)$ {  # OOPS!
  proxy_pass http://luscious/$2$is_args$args;  # OOPS!
}

วิธีแก้ปัญหาอาจดูน่ารักกว่าตอนที่เห็น แต่มันผิดด้วยเหตุผลหลายประการ

  • regex ด้านบนจะตรงกับคำขอ uri ของ/enjoyเปลี่ยนเส้นทางไปยัง/joyอัปสตรีม มันตั้งใจจริงเหรอ?

  • คำขอ/enจะไม่ส่งผลให้มีการเปลี่ยนเส้นทางใด ๆ ซึ่งเป็นการให้บริการโดยตรง/จาก upstream (เกือบจะเหมือนกับว่ามีการร้องขอ/en/แทน แต่ไม่มากนัก) หากคุณใช้ URIs ที่เกี่ยวข้องในรูทเพจของคุณ upstream (ไม่เช่นนั้นทำไมคุณไม่มี/en/คำนำหน้าอยู่ใน URIs ที่อยู่ด้านบน) เช่นsrc="style.css"(ซึ่งอาจอ้างอิงภาษาเฉพาะurl("menu.png")เป็นต้น) เบราว์เซอร์จะขอให้ เป็นแทน/style.css /en/style.css(หรือแม้ว่าคุณจะใช้ URIs แบบสัมบูรณ์ทุกที่จะเกิดอะไรขึ้นถ้ามีคนอ้างอิงทรัพยากรกึ่งทางเลือกที่ไม่ชัดเจนค่อนข้างโอ๊ะโอไซต์ก็อาจใช้งานไม่ได้ แต่บางครั้งหรือในกรณีที่เป็นขอบ

  • ตามคำแนะนำก่อนหน้านี้ของฉันในคำถามอื่นที่ได้กล่าวถึงแล้วโดยคำตอบของ OP การใช้นิพจน์ทั่วไปจะป้องกันไม่ให้proxy_redirectคำสั่งมีค่าเริ่มต้นdefaultหันลงมาoffแทน ซึ่งหมายความว่าหาก upstream ตอบกลับด้วยLocation: http://127.0.0.1:8080/en/dir/เมื่อมีการร้องขอให้/en/dirทำนั่นคือสิ่งที่ลูกค้าจะเห็นซึ่งเห็นได้ชัดว่าทำงานไม่ถูกต้อง (ซึ่งจะเป็นเรื่องน่าขันโดยเฉพาะอย่างยิ่งสำหรับ/enคำขอที่พร้อมท์ให้ใช้ regex ตั้งแต่แรก แต่การใช้งานเฉพาะนี้กลับทนทุกข์ทรมานจากปัญหาอื่นตามที่ได้กล่าวไว้แล้วข้างต้น) นอกจากนี้หากคุณกำลังใช้upstreamคำสั่งนั้นอาจเพิ่มขึ้นอย่างน่าเกลียดหากคุณเพียงแค่ลองใช้เซิร์ฟเวอร์ที่กำหนดเองโดยเฉพาะอย่างยิ่งถ้าคุณมีเซิร์ฟเวอร์ upstream มากกว่าหนึ่งเครื่อง - คุณมีวิธีแยกกันอย่างไรproxy_redirectสำหรับแต่ละเซิร์ฟเวอร์ คุณสามารถใช้นิพจน์ทั่วไปภายในproxy_redirectหรือแม้แต่จับคู่กับโฮสต์ใดก็ได้ แต่ถ้าหากคุณตัดสินใจเปลี่ยนเส้นทางข้ามโดเมนในอนาคต

เพื่อพยายามที่จะอยู่บางจุดดังกล่าวมีสถานที่ regex-based เดียวที่เราจะทำต่อไปนี้ (โปรดทราบว่าในการproxy_passที่เรายังมีการวางการอ้างอิงไปยังเซิร์ฟเวอร์จากupstreamคำสั่งชั่นเพื่อให้proxy_redirectตรงไปตรงมามากกว่า):

location ~ ^/en/?((?<=/).*)?$ {
  location = /en { return 302 /en/; }
  proxy_pass http://127.0.0.1:8080/$1$is_args$args;
  proxy_redirect http://127.0.0.1:8080/ /en/;
}

ดังนั้นถ้าคุณถามฉันโซลูชันดั้งเดิมที่มีตำแหน่งระดับบนสุดของพี่น้องสองคนจะยังคงเป็นความคิดที่ดีกว่าการขุดตัวเองลงในโพรงกระต่ายโดยไปที่เส้นทาง regex แทน


สิ่งนี้ทำให้เกิดข้อยกเว้น:nginx: [emerg] location "/en" is outside location "^/en/?((?<=/).*
Athlan

1
@Athlan นั่นเป็นเพราะคุณไม่ควรใช้สิ่งนั้นตั้งแต่แรก! หากคุณยังต้องการคุณสามารถวางตำแหน่งนั้นไว้นอก regexp
cnst

วาง localation = / en นอกทำงานได้ดีจริงๆ! ขอบคุณ
Sebastian Webber

7

ดังนั้นฉันพบคำตอบใน stackoverflow :

upstream luscious {
 server lixxxx.members.linode.com:9001;
}

server {
  root /var/www/example.com/current/public/;
  server_name example.com;

  location ~ ^/en(/?)(.*) {
    proxy_pass http://luscious/$2;
  }
}

โดยทั่วไป: ส่ง regex ไปยังที่ตั้งและส่ง backref ไปที่ proxy_pass url


ฉันคิดว่า "proxy_pass luscious / $ ;" ควรเป็น "proxy_pass luscious / $ 2 "
Zafer

1
@Zafer ถูกต้องคำตอบข้างต้นทำให้ฉันมีข้อผิดพลาด
franck

ฉันเปลี่ยนคำตอบแล้ว แต่ไม่มีเซิร์ฟเวอร์ที่ฉันสามารถลองได้ ATM จะไม่ได้รับการยืนยัน
berkes

0

การบัญชีไปยังเอกสาร Nginx

ในการส่งคำร้องขอไปยังเซิร์ฟเวอร์ HTTP proxied คำสั่ง proxy_pass จะถูกระบุไว้ในสถานที่ ตัวอย่างเช่น:

location /some/path/ {
    proxy_pass http://www.example.com/link/;
}

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

location ~ \.php {
    proxy_pass http://127.0.0.1:8000;
}

โปรดทราบว่าในตัวอย่างแรกข้างต้นที่อยู่ของเซิร์ฟเวอร์พร็อกซีจะตามด้วย URI, / link / หากระบุ URI พร้อมกับที่อยู่จะแทนที่ส่วนของ URI คำขอที่ตรงกับพารามิเตอร์ตำแหน่ง ยกตัวอย่างเช่นที่นี่คำขอกับ /some/path/page.html URI จะ proxied เพื่อhttp://www.example.com/link/page.html หากมีการระบุที่อยู่โดยไม่มี URI หรือไม่สามารถระบุส่วนของ URI ที่จะถูกแทนที่ได้ URI คำขอแบบเต็มจะถูกส่งผ่าน (อาจเป็นไปได้ว่ามีการแก้ไข)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.