Nginx Redirect qua Proxy, Rewrite và bảo toàn URL


71

Trong Nginx, chúng tôi đã cố gắng chuyển hướng một URL như sau:

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

nơi người dùng vẫn thấy URL gốc trong trình duyệt của họ. Khi người dùng được chuyển hướng, giả sử họ nhấp vào liên kết đến /section/index.html, chúng tôi muốn điều này đưa ra yêu cầu dẫn đến chuyển hướng

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

và một lần nữa vẫn bảo tồn URL gốc.

Các nỗ lực của chúng tôi đã liên quan đến các giải pháp khác nhau bằng cách sử dụng proxy và quy tắc viết lại, và bên dưới cho thấy cấu hình đã đưa chúng tôi đến gần nhất với một giải pháp (lưu ý rằng đây là cấu hình máy chủ web cho example.commáy chủ web). Tuy nhiên, vẫn còn hai vấn đề với điều này:

  • Nó không thực hiện viết lại đúng cách, trong đó URL yêu cầu mà máy chủ web nhận được http://192.168.1.24bao gồm /some/pathvà do đó không phục vụ trang được yêu cầu.
  • Khi bạn di chuột vào một liên kết khi một trang đã được phục vụ, /some/paththiếu 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;
        }
    }
    

Chúng tôi đang tìm kiếm một giải pháp chỉ liên quan đến việc thay đổi cấu hình máy chủ web trên example.com. Chúng tôi có thể thay đổi cấu hình trên 192.168.1.24(cũng là Nginx), tuy nhiên chúng tôi muốn thử và tránh điều này vì chúng tôi sẽ cần lặp lại thiết lập này cho hàng trăm máy chủ khác nhau có quyền truy cập được ủy quyền example.com.

Câu trả lời:


59

Đầu tiên, bạn không nên sử dụng rootchỉ thị bên trong khối vị trí, đó là một thực tế xấu. Trong trường hợp này nó không quan trọng mặc dù.

Hãy thử thêm một khối vị trí thứ hai:

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

Phần này ghi lại phần sau / some / path / và trước index.html thành biến phần $, sau đó được sử dụng để đặt đích proxy_pass. Bạn có thể làm cho regex cụ thể hơn nếu bạn yêu cầu.


1
Xin lỗi vì đã trả lời trễ - điều này rất gần với việc đạt được những gì chúng tôi đang tìm kiếm. Thiếu sót duy nhất là, khi trang đích đã được phục vụ, các URL cho các liên kết trong trình duyệt không bao gồm '/ some / path /' trong đó, nghĩa là chúng không hoạt động nếu người dùng nhấp vào chúng. Nếu chúng ta có thể tìm ra cách khắc phục điều này, tôi sẽ cập nhật và chấp nhận câu trả lời này, vì nó gần như vậy rồi.
robjohncox

8
Các liên kết mà trình duyệt nhìn thấy được tạo bởi phần mềm đang chạy trên máy chủ 192.168.1.24. Bạn nên sửa đổi phần mềm đó để đạt được những gì bạn muốn.
Tero Kilkanen

không chắc chắn tôi làm theo cảnh báo của bạn về root bên trong khối vị trí. đọc tài liệu nginx đó là cách đúng đắn để làm công cụ. họ chỉ cảnh báo thực tiễn xấu từ việc không có root mặc định bên ngoài tất cả các vị trí. nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/...
anh chàng mograbi

Chà, sẽ dễ dàng hơn khi có một quy tắc không sử dụng rootbên trong một locationkhối, sau đó bạn sẽ không nhận được bất kỳ hành vi bất ngờ nào cho các vị trí mặc định. Chỉ khi bạn cần thay đổi mặc định rootcho từng vị trí, thì bạn mới có thể sử dụng nó.
Tero Kilkanen

1
Bạn có ý nghĩa gì khi nhận được máy chủ $ dưới dạng tên ? Tiêu đề HTTP chính xác đã được gửi là gì và chính xác bạn muốn nó gửi cái gì?
Tero Kilkanen

65

Bạn nên sử dụng một phần URI trong proxy_passchỉ thị. Ngoài ra, bạn đã trộn lẫn các đối số thứ tự của proxy_redirectchỉ thị và có lẽ bạn hoàn toàn không cần nó. Nginx có mặc định hợp lý cho chỉ thị này.

Trong trường hợp này, locationkhối của bạn có thể thực sự đơn giản:

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

1
Xin lỗi vì đã trả lời trễ - Tôi đã thử điều này và thật không may, nó không hoạt động cho trường hợp sử dụng của chúng tôi. Vấn đề là, khi yêu cầu được thực hiện trên máy chủ đích, /some/path/một phần của URL được giữ nguyên trong yêu cầu không phải là URL hợp lệ (chúng tôi cũng cần phải viết lại URL để loại bỏ URL này).
robjohncox

@robjohncox chính xác những gì bạn đã thử?
Alexey Ten

9
dấu gạch chéo đã lừa tôi. Bây giờ mydomain.com/some/path/* được ủy quyền chính xác thành 192.168.1.24/* chứ không phải 192.168.1.24/some/path/*
Vadimo

7
Tôi có thể nâng cao nhận xét "# note this slash" trong phản hồi này không? Ba cổ vũ cho nhận xét đó!
8one6

Không chắc làm thế nào điều này đang làm việc cho tất cả các bạn. Đây là những gì tôi đang cố gắng để đạt được. Tuy nhiên, khi người dùng nhấp vào một liên kết có thể chuyển hướng, ví dụ như 192.168.1.24/login trên dịch vụ địa phương, anh ta được chuyển hướng đến mydomain.com/login thay vì mydomain.com/some/path/login
mueslo

4

Bạn có thể sử dụng cấu hình sau để có ánh xạ liền mạch 100% giữa /some/path/mặt trước và /mặt sau.

Lưu ý rằng đây là câu trả lời duy nhất cho đến nay cũng sẽ xử lý liền mạch các 404 Not Foundlỗi tạo đường dẫn tuyệt đối , miễn là Referertiêu đề HTTP chính xác được gửi bởi trình duyệt, vì vậy, tất cả các gifs đó sẽ tiếp tục tải mà không cần sửa đổi HTML cơ bản (không chỉ đắt tiền, mà còn không được hỗ trợ nếu không có các mô-đun bổ sung không được biên dịch theo mặc định).

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

Bạn có thể tìm thấy đầy đủ bằng chứng về khái niệm và sản phẩm khả thi tối thiểu trong kho https://github.com/cnst/StackOverflow.cnst.nginx.conf .

Đây là một lần chạy thử để xác nhận rằng tất cả các trường hợp cạnh dường như hoạt động:

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

PS Nếu bạn có nhiều đường dẫn khác nhau để lập bản đồ, thì thay vì thực hiện so sánh regex $http_refererbên iftrong location @404, bạn có thể muốn sử dụng mapchỉ thị dựa trên toàn cầu thay thế.

Cũng lưu ý rằng dấu gạch chéo trong cả hai proxy_pass, cũng như locationnó được chứa trong, là khá quan trọng theo câu trả lời liên quan .

Người giới thiệu:


2

Khi dấu gạch chéo đó được thêm vào một jenkins được ủy quyền nginx, bạn sẽ được trình bày với bản quyền. Có vẻ như thiết lập proxy ngược của bạn bị hỏng ".

proxy_pass          http://localhost:8080/;

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

Nó nên đọc

proxy_pass          http://localhost:8080;

Tôi không nghĩ rằng đây là những gì câu hỏi của OP có liên quan và cũng không giải quyết được bất kỳ vấn đề nào được đề cập.
Cory Robinson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.