Nginx Redirect ผ่าน Proxy, Rewrite และ Preserve URL


71

ใน Nginx เราพยายามเปลี่ยนเส้นทาง URL ดังนี้:

http://example.com/some/path -> http://192.168.1.24

ตำแหน่งที่ผู้ใช้ยังคงเห็น URL ดั้งเดิมในเบราว์เซอร์ของตน เมื่อผู้ใช้ถูกเปลี่ยนเส้นทางกล่าวว่าพวกเขาคลิกที่ลิงค์ไป/section/index.htmlเราต้องการให้คำขอที่นำไปสู่การเปลี่ยนเส้นทาง

http://example.com/some/path/section/index.html -> http://192.168.1.24/section/index.html

และอีกครั้งยังคงรักษา URL เดิม

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

  • มันไม่ได้เขียนใหม่อย่างถูกต้องในที่ URL คำขอที่ได้รับจากเว็บเซิร์ฟเวอร์http://192.168.1.24รวมถึง/some/pathและดังนั้นจึงไม่สามารถให้บริการหน้าที่จำเป็น
  • เมื่อคุณวางบนลิงค์เมื่อเพจได้รับการบริการ/some/pathหายไปจาก URL

    server {
        listen          80;
        server_name     www.example.com;
    
        location /some/path/ {
            proxy_pass http://192.168.1.24;
            proxy_redirect http://www.example.com/some/path http://192.168.1.24;
            proxy_set_header Host $host;
        }
    
        location / {
            index index.html;
            root  /var/www/example.com/htdocs;
        }
    }
    

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

คำตอบ:


59

ขั้นแรกคุณไม่ควรใช้rootคำสั่งภายในบล็อกสถานที่ แต่เป็นการปฏิบัติที่ไม่ดี ในกรณีนี้มันไม่สำคัญว่า

ลองเพิ่มบล็อกตำแหน่งที่สอง:

location ~ /some/path/(?<section>.+)/index.html {
    proxy_pass http://192.168.1.24/$section/index.html;
    proxy_set_header Host $host;
}

สิ่งนี้จะจับส่วนหลัง / some / path / และก่อน index.html ไปยังตัวแปร $ section ซึ่งจะถูกใช้เพื่อตั้งค่าปลายทาง proxy_pass คุณสามารถทำให้ regex เฉพาะเจาะจงมากขึ้นถ้าคุณต้องการ


1
ขอโทษสำหรับการตอบกลับปลาย - นี่คือเพื่อให้ใกล้ที่จะบรรลุสิ่งที่เรากำลังมองหา ข้อบกพร่องเพียงอย่างเดียวคือเมื่อหน้าเป้าหมายได้รับการบริการแล้ว URL สำหรับลิงก์ในเบราว์เซอร์จะไม่รวม '/ some / path /' ในพวกเขาซึ่งหมายความว่าพวกเขาจะไม่ทำงานหากผู้ใช้คลิกที่พวกเขา หากเราสามารถหาวิธีที่จะเอาชนะสิ่งนี้ฉันจะอัปเดตและยอมรับคำตอบนี้เพราะมันใกล้จะถึงเท่านั้น
robjohncox

8
ลิงค์ที่เบราว์เซอร์เห็นนั้นสร้างขึ้นโดยซอฟต์แวร์ที่ทำงานบนเซิร์ฟเวอร์ 192.168.1.24 คุณควรปรับเปลี่ยนซอฟต์แวร์เพื่อให้บรรลุสิ่งที่คุณต้องการ
Tero Kilkanen

ไม่แน่ใจว่าฉันทำตามคำเตือนของคุณเกี่ยวกับรูทในบล็อกตำแหน่ง อ่านเอกสาร nginx มันเป็นวิธีที่เหมาะสมในการทำสิ่งต่าง ๆ พวกเขาเพียงเตือนการปฏิบัติที่ไม่ดีไม่ให้รูทเริ่มต้นอยู่นอกสถานที่ nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/ …
ผู้ชาย mograbi

มันง่ายกว่าที่จะมีกฎง่ายๆที่จะไม่ใช้rootภายในlocationบล็อกจากนั้นคุณจะไม่ได้รับพฤติกรรมที่ไม่คาดคิดสำหรับตำแหน่งเริ่มต้น เฉพาะในกรณีที่คุณต้องการเปลี่ยนค่าเริ่มต้นrootสำหรับแต่ละสถานที่จากนั้นคุณสามารถใช้งานได้
Tero Kilkanen

1
คุณหมายถึงอะไรโดยรับ $ host เป็นชื่อ ? ส่วนหัว HTTP ที่แน่นอนที่ถูกส่งคืออะไรและสิ่งที่คุณต้องการที่จะส่ง?
Tero Kilkanen

65

คุณควรใช้ส่วน URI ในproxy_passคำสั่ง นอกจากนี้คุณยังผสมอาร์กิวเมนต์proxy_redirectคำสั่งของคำสั่งและคุณอาจไม่ต้องการมันเลย Nginx มีค่าเริ่มต้นที่สมเหตุสมผลสำหรับคำสั่งนี้

ในกรณีนี้การlocationบล็อกของคุณอาจเป็นเรื่องง่าย:

location /some/path/ {
    proxy_pass http://192.168.1.24/;
    # note this slash  -----------^
    proxy_set_header Host $host;
}

1
ขออภัยในการตอบกลับล่าช้า - ฉันลองทำแล้ว แต่น่าเสียดายที่มันใช้ไม่ได้กับกรณีการใช้งานของเรา ปัญหาคือเมื่อมีการร้องขอบนเซิร์ฟเวอร์เป้าหมาย/some/path/ส่วนของ URL นั้นจะถูกเก็บไว้ในคำขอซึ่งไม่ใช่ URL ที่ถูกต้อง (เราจำเป็นต้องเขียน URL ใหม่เพื่อลบสิ่งนี้)
robjohncox

@robjohncox คุณลองทำอะไรกันแน่?
Alexey Ten

9
สแลชใช้กลอุบายของฉัน ตอนนี้ mydomain.com/some/path/* พร็อกซีถูกต้องเป็น 192.168.1.24/* และไม่ใช่ 192.168.1.24/some/path/*
Vadimo

7
ฉันสามารถโหวตความคิดเห็น "# note slash นี้" ในการตอบกลับนี้ได้หรือไม่ สามไชโยสำหรับความคิดเห็นนั้น!
8one6

ไม่แน่ใจว่าวิธีนี้ใช้ได้ผลกับคุณทุกคน นี่คือสิ่งที่ฉันพยายามที่จะบรรลุ อย่างไรก็ตามเมื่อผู้ใช้คลิกลิงก์ที่จะเปลี่ยนเส้นทางเช่นไปยัง 192.168.1.24/login ในบริการท้องถิ่นผู้ใช้จะถูกเปลี่ยนเส้นทางไปยัง mydomain.com/login แทน mydomain.com/some/path/login
mueslo

4

คุณสามารถใช้การกำหนดค่าต่อไปนี้เพื่อให้ได้การทำแผนที่ที่ไร้รอยต่อ 100% ระหว่าง/some/path/ส่วนหน้าและ/ส่วนหลัง

โปรดทราบว่านี่เป็นคำตอบเดียวที่จะดูแลเส้นทางที่สร้าง404 Not Foundข้อผิดพลาดได้อย่างไร้รอยต่อRefererโดยที่เบราว์เซอร์ส่วนหัวHTTP ที่ถูกต้องถูกส่งโดยเบราว์เซอร์ดังนั้น gif เหล่านั้นทั้งหมดควรโหลดต่อโดยไม่จำเป็นต้องแก้ไข HTML พื้นฐาน (ซึ่งไม่เพียง แต่มีราคาแพง แต่ยังไม่รองรับหากไม่มีโมดูลเพิ่มเติมที่ไม่ได้รวบรวมโดยค่าเริ่มต้น)

location /some/path/ {
    proxy_pass http://192.168.1.24/; # note the trailing slash!
}
location / {
    error_page 404 = @404;
    return 404; # this would normally be `try_files` first
}
location @404 {
    add_header Vary Referer; # sadly, no effect on 404
    if ($http_referer ~ ://[^/]*(/some/path|/the/other)/) {
        return 302 $1$uri;
    }
    return 404 "Not Found\n";
}

คุณสามารถค้นหาหลักฐานพิสูจน์แนวคิดและผลิตภัณฑ์ที่มีศักยภาพน้อยที่สุดได้ในhttps://github.com/cnst/StackOverflow.cnst.nginx.conf ที่เก็บ

นี่คือการทดสอบเพื่อยืนยันว่ากรณีขอบทั้งหมดทำงานได้ดี:

curl -v -H 'Referer: http://example.su/some/path/page.html' localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
> Referer: http://example.su/some/path/page.html
< HTTP/1.1 302 Moved Temporarily
< Location: http://localhost:6586/some/path/and/more.gif
< Vary: Referer

curl -v localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
< HTTP/1.1 404 Not Found

curl -v localhost:6586/some/path/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location -e uri
> GET /some/path/and/more.gif HTTP/1.1
< HTTP/1.1 200 OK
request_uri:    /and/more.gif

ป.ล. หากคุณมีเส้นทางที่แตกต่างไปยังแผนที่จำนวนมากดังนั้นแทนที่จะทำการเปรียบเทียบ regex $http_refererภายในifภายในlocation @404คุณอาจต้องการใช้mapคำสั่งพื้นฐานแบบโกลบอลแทน

นอกจากนี้ทราบว่าต่อท้าย slashes ในทั้งสองproxy_passเช่นเดียวกับlocationที่มันมีอยู่ในที่ค่อนข้างสำคัญเป็นต่อคำตอบที่เกี่ยวข้อง

อ้างอิง:


2

เมื่อเครื่องหมายสแลชนั้นถูกเพิ่มเข้าไปในข้อผิดพลาดของ nginx jenkins คุณจะพบกับข้อผิดพลาด“ ปรากฏว่าข้อผิดพลาดพร็อกซีการตั้งค่าย้อนกลับของคุณเสีย”

proxy_pass          http://localhost:8080/;

Remove this -----------------------------^

ควรอ่าน

proxy_pass          http://localhost:8080;

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