Nginx reverse proxy + URL เขียนใหม่


149

Nginx ทำงานบนพอร์ต 80 และฉันใช้เพื่อย้อนกลับ URL พร็อกซีด้วยพา ธ/fooไปยังพอร์ต3200ด้วยวิธีนี้:

location /foo {
                proxy_pass http://localhost:3200;
                proxy_redirect     off;
                proxy_set_header   Host $host;
}

ใช้งานได้ดี แต่ฉันมีแอปพลิเคชันบนพอร์ต3200ซึ่งฉันไม่ต้องการ/fooให้ส่งเริ่มต้นไปที่ นั่นคือ - เมื่อฉันเข้าถึงhttp://localhost/foo/barฉันต้องการเพียง/barเป็นเส้นทางตามที่ได้รับจากแอพ ดังนั้นฉันพยายามเพิ่มบรรทัดนี้ในบล็อกตำแหน่งที่ตั้งด้านบน:

rewrite ^(.*)foo(.*)$ http://localhost:3200/$2 permanent;

สิ่งนี้ทำให้เกิดการเปลี่ยนเส้นทาง 302 (เปลี่ยนเป็น URL) แต่ฉันต้องการ 301 ฉันควรทำอย่างไร


หากคุณมีปัญหาใด ๆ กับกรณี Grafana คุณควรใช้สูตรเหล่านี้: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

คำตอบ:


168

การเปลี่ยนเส้นทางใด ๆ ไปยังโลคอลโฮสต์ไม่เหมาะสมจากระบบระยะไกล (เช่นเว็บเบราว์เซอร์ของลูกค้า) ดังนั้นแฟล็กการเขียนซ้ำอย่างถาวร (301) หรือการเปลี่ยนเส้นทาง (302) จึงไม่สามารถใช้ได้ในกรณีของคุณ

โปรดลองทำตามการตั้งค่าโดยใช้กฎการเขียนซ้ำแบบโปร่งใส:

location  /foo {
  rewrite /foo/(.*) /$1  break;
  proxy_pass         http://localhost:3200;
  proxy_redirect     off;
  proxy_set_header   Host $host;
}

ใช้curl -iสำหรับทดสอบการเขียนซ้ำของคุณ การเปลี่ยนแปลงกฎเล็กน้อยอาจทำให้ nginx ทำการเปลี่ยนเส้นทาง


1
เส้นทาง URL ยังคงเริ่มต้นด้วย / foo ใน app ของฉันเมื่อฉันทำอย่างนั้น ...
jeffreyveon

จะต้องมีปัญหาที่แตกต่าง ฉันทำซ้ำสถานการณ์นี้สำเร็จเมื่อไม่กี่นาทีที่ผ่านมา URL ดั้งเดิม: http: // development / foo / testme / 1234 - REQUEST_URI ของสคริปต์ PHP ที่ทำงานบน Apache ที่เชื่อมต่อเป็นพร็อกซีแบ็คเอนด์: '/ testme / 1234'
Jens Bradler

9
regex น่าจะเป็น/foo(.*)มิฉะนั้นexample.com/fooจะไม่ถูกจับคู่ (ซึ่งอาจเป็นสิ่งที่เจฟฟรีย์มีประสบการณ์)
Benno

งานประเภทนี้ แต่การตั้งค่าร่างกายของฉันฉันกำลังลบ proxy_set_body
Justin Thomas

เขียนใหม่ /(.*) /socket.io/ ตัวแบ่ง; บันทึกวันของฉันสำหรับ SOCKET.IO
user956584

123

การจับคู่คำนำหน้าตำแหน่งอย่างง่ายสามารถใช้ได้กับสิ่งนี้โดยไม่ต้องใช้กฎการเขียนซ้ำตราบใดที่คุณระบุ URI ในคำสั่ง proxy_pass:

location /foo {
  proxy_pass http://localhost:3200/;
}

แจ้งให้ทราบเพิ่มเติม/ในตอนท้ายของproxy_passคำสั่ง NGINX จะตัดคำนำหน้าจับคู่/fooและผ่านที่เหลือไปยังเซิร์ฟเวอร์แบ็กเอนด์ที่ /URI ดังนั้นจะโพสต์ไปที่แบ็กเอนด์http://myserver:80/foo/barhttp://localhost:3200/bar

จากเอกสาร NGINX บน proxy_pass :

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


12
ทำงานให้ฉันได้มากกว่าที่ฉันเพิ่ม / ไปยังสถานที่ / foo / {
Andrei N

นี่คือสิ่งที่ฉันกำลังมองหา!
anbiniyar

6
นี่เป็นคำตอบที่สะอาดมากฉันอยากให้นี่เป็นคำตอบที่ยอมรับได้สำหรับคำถาม
ralien

2
ใช้เวลานานเกินไปที่จะตระหนักถึงความสำคัญของการรักษาหรือลบเครื่องหมายทับ
Parvez

5
สิ่งนี้จะส่งผ่าน//xyzไปยังโฮสต์หากคุณทำเช่นนั้น
อาร์คิมีดีสทราโนโน

60

วิธีที่ถูกต้องที่สุดและการปฏิบัติที่ดีที่สุดมักจะเป็นดังนี้:

location /foo/ {
    proxy_pass http://localhost:3200/; # note the trailing slash!
}

  • สังเกตความสำคัญของสแลชต่อท้ายproxy_passซึ่งจะเปลี่ยนแปลง$uriตัวแปรโดยอัตโนมัติเพื่อให้ฟรอนต์เอนด์/foo/สอดคล้องกับ/แบ็กเอนด์ ไม่จำเป็นต้องมีrewriteคำสั่งที่ชัดเจน

  • นอกจากนี้ทราบว่าที่ต่อท้าย/ในlocationเป็นสิ่งสำคัญมากเช่นกัน - ไม่ว่าคุณมีความเสี่ยงที่มี URL ที่แปลกที่ดูในเว็บไซต์ของคุณเมื่อถึงจุดหนึ่ง (เช่นการทำงานที่/fooenนอกเหนือไป/foo/en)

    นอกจากนี้ต่อท้าย/ในlocationกับproxy_passยังช่วยให้บางจัดการพิเศษตามเอกสารของlocationคำสั่งที่มีประสิทธิภาพทำให้เกิดนัยlocation = /foo {return 301 /foo/;}เช่นกัน

    ดังนั้นโดยการกำหนดlocationด้วยการเฉือนท้ายกับข้างต้นคุณไม่เพียง แต่ให้มั่นใจว่าเฉือนน้อย URL ที่ต่อท้ายเหมือน/fooenจะไม่ถูกต้อง แต่ยังเป็นที่/fooโดยไม่ต้องเฉือนท้ายจะยังคงทำงานได้เป็นอย่างดี


เอกสารอ้างอิง:


ดูเหมือนว่า$argsจะสูญหาย: http://frontend/foo?bar=bazจะถูกพร็อกhttp://backend/ซี โปรดสังเกตว่า args ไม่ใช่ส่วนหนึ่งของ url
Vanuan

@Vanuan คุณแน่ใจหรือไม่ ฉันรักบางอย่าง$argsจะยังคงได้รับการจัดการอย่างเหมาะสมถ้าคุณใช้โค้ดข้างต้นขณะที่พวกเขากำลังแยกออกจากและควรได้รับการประกอบกลับมาอยู่ในถ้าคุณกำลังใช้ตัวแปรที่ชัดเจนในของคุณ$uri proxy_pass
cnst

@cnst โอ้ฉันเข้าใจ ฉันใช้ตัวแปรโฮสต์ นั่นเป็นเคาน์เตอร์ที่ใช้งานง่าย
Vanuan

1
@ArchimedesTrajano คุณไม่ถูกต้องเนื่องจากมีการจัดการพิเศษสำหรับ/fooการเปลี่ยนเส้นทางไป/foo/ดังนั้นถ้าคุณไม่ได้ทำอะไรแปลก ๆ ในแบ็กเอนด์แม้แต่/fooคำขอก็ยังใช้งานได้กับรหัสข้างต้น (นี่เป็นส่วนหนึ่งของคำตอบแล้ว BTW.)
cnst

3
ในขณะที่การแก้ปัญหานี้ "ดูเหมือน" จะชนะในฟอรัมเหล่านี้ทั้งหมด แต่ก็ควรจดบันทึกไว้ในคำตอบด้วยตัวเองว่า Nginx จะ URL ถอดรหัส URL และส่ง URL ถอดรหัสไปยังเซิร์ฟเวอร์ proxied ดังนั้นโซลูชันนี้จะไม่ทำงานหาก URL ของคุณมีส่วนที่เข้ารหัส URL
การเข้ารหัส

1

ลอง

location /foo {
    proxy_pass http://localhost:3200/;
    ....

หรือ

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....

13
คำตอบนี้จะดีถ้าคุณให้คำอธิบายว่าทำไมต้องมีการกำหนดค่าดังกล่าวข้างต้น
masegaloeh

สิ่งนี้จะส่งผ่าน//xyzไปยังโฮสต์หากคุณทำเช่นนั้น
อาร์คิมีดีสทราโนโน

1

@Terabuck ขออภัยที่ยังไม่ได้ตอบตัวแทนยัง

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

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

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