Nginx Redirect melalui Proxy, Rewrite, dan Preserve URL

71

Di Nginx kami telah mencoba mengarahkan ulang URL sebagai berikut:

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

di mana pengguna masih melihat URL asli di browser mereka. Setelah pengguna diarahkan, misalkan mereka mengklik tautan /section/index.html, kami ingin ini membuat permintaan yang mengarah ke pengalihan

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

dan sekali lagi masih mempertahankan URL asli.

Upaya kami telah melibatkan berbagai solusi menggunakan proxy dan aturan penulisan ulang, dan di bawah ini menunjukkan konfigurasi yang telah membawa kami paling dekat ke solusi (perhatikan bahwa ini adalah konfigurasi server web untuk example.comserver web). Namun, masih ada dua masalah dengan ini:

  • Itu tidak melakukan penulisan ulang dengan benar, karena URL permintaan yang diterima oleh server web http://192.168.1.24termasuk /some/pathdan karena itu gagal untuk melayani halaman yang diperlukan.
  • Saat Anda mengarahkan kursor ke tautan setelah halaman disajikan, /some/pathhilang dari 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;
        }
    }
    

Kami mencari solusi yang hanya melibatkan pengubahan konfigurasi server web example.com. Kami dapat mengubah konfigurasi pada 192.168.1.24(juga Nginx), namun kami ingin mencoba dan menghindari ini karena kami perlu mengulangi pengaturan ini untuk ratusan server yang berbeda yang aksesnya diproksikan melalui example.com.

robjohncox
sumber

Jawaban:

59

Pertama, Anda tidak boleh menggunakan rootarahan di dalam blok lokasi, ini adalah praktik yang buruk. Dalam hal ini tidak masalah.

Coba tambahkan blok lokasi kedua:

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

Ini menangkap bagian setelah / some / path / dan sebelum index.html ke variabel $ section, yang kemudian digunakan untuk menetapkan tujuan proxy_pass. Anda dapat membuat regex lebih spesifik jika Anda memerlukannya.

Tero Kilkanen
sumber
1
Permintaan maaf atas jawaban yang terlambat - ini sangat dekat untuk mencapai apa yang kita cari. Satu-satunya kekurangan adalah bahwa, setelah halaman target telah dilayani, URL untuk tautan di browser tidak menyertakan '/ some / path /' di dalamnya, yang berarti bahwa mereka tidak berfungsi jika pengguna mengkliknya. Jika kita dapat mengetahui cara mengatasi ini, saya akan memperbarui dan menerima jawaban ini, karena sudah hampir tiba.
robjohncox
8
Tautan yang dilihat browser dihasilkan oleh perangkat lunak yang berjalan pada server 192.168.1.24. Anda harus memodifikasi perangkat lunak itu untuk mencapai apa yang Anda inginkan.
Tero Kilkanen
tidak yakin saya mengikuti peringatan Anda tentang root di dalam blok lokasi. membaca dokumentasi nginx itu adalah cara yang tepat untuk melakukan sesuatu. mereka hanya memperingatkan praktik buruk karena tidak memiliki root default di luar semua lokasi. nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/…
guy mograbi
Nah, lebih mudah memiliki aturan praktis untuk tidak menggunakan rootdi dalam locationblok, maka Anda tidak akan mendapatkan perilaku yang tidak terduga untuk lokasi default. Hanya jika Anda perlu mengubah default rootuntuk setiap lokasi, maka Anda dapat menggunakannya.
Tero Kilkanen
1
Apa yang Anda maksud dengan menerima $ host sebagai nama ? Apa header HTTP persis yang dikirim dan apa yang sebenarnya ingin Anda kirim?
Tero Kilkanen
65

Anda harus menggunakan bagian URI dalam proxy_passarahan. Juga, Anda campur aduk argumen proxy_redirectarahan, dan mungkin Anda tidak membutuhkannya sama sekali. Nginx memiliki standar yang masuk akal untuk arahan ini.

Dalam hal ini, locationblok Anda bisa sangat sederhana:

location /some/path/ {
    proxy_pass http://192.168.1.24/;
    # note this slash  -----------^
    proxy_set_header Host $host;
}
Alexey Ten
sumber
1
Permintaan maaf atas jawaban yang terlambat - Saya mencoba ini dan sayangnya itu tidak berfungsi untuk kasus penggunaan kami. Masalahnya adalah, ketika permintaan dibuat pada server target, /some/path/bagian dari URL disimpan dalam permintaan yang bukan URL yang valid (kita perlu menulis ulang URL juga untuk menghapus ini).
robjohncox
@robjohncox apa sebenarnya yang Anda coba?
Alexey Ten
9
tebasan itu membantu saya. Sekarang mydomain.com/some/path/* diproksi dengan benar ke 192.168.1.24/* dan bukan 192.168.1.24/some/path/*
Vadimo
7
Dapatkah saya membatalkan komentar "# note this slash" dalam respons ini? Tiga sorakan untuk komentar itu!
8one6
Tidak yakin bagaimana ini bekerja untuk Anda semua. Inilah yang ingin saya capai. Namun, ketika pengguna mengklik tautan yang akan mengarahkan misalnya ke 192.168.1.24/login di layanan lokal, ia diarahkan ke mydomain.com/login alih-alih mydomain.com/some/path/login
mueslo
4

Anda dapat menggunakan konfigurasi berikut untuk memiliki pemetaan mulus 100% antara /some/path/di front-end dan /di backend.

Perhatikan bahwa ini adalah satu-satunya jawaban sejauh ini yang juga akan menjaga 404 Not Foundkesalahan jalur mutlak menghasilkan kesalahan, asalkan Refererheader HTTP yang benar dikirim oleh browser, jadi, semua gif harus terus memuat tanpa perlu memodifikasi HTML yang mendasarinya (yang tidak hanya mahal, tetapi juga tidak didukung tanpa modul tambahan tidak dikompilasi secara default).

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

Anda dapat menemukan bukti-konsep-lengkap dan produk minimal-layak dalam https://github.com/cnst/StackOverflow.cnst.nginx.conf repositori.

Berikut ini adalah uji coba untuk mengonfirmasi bahwa semua kasus tepi tampaknya berfungsi:

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

NB. Jika Anda memiliki banyak jalur berbeda untuk dipetakan, maka alih-alih melakukan perbandingan regex $http_refererdi ifdalam location @404, Anda mungkin ingin menggunakan maparahan berbasis global sebagai gantinya.

Perhatikan juga bahwa garis miring di kedua proxy_pass, dan juga di locationdalamnya, cukup penting sesuai jawaban terkait .

Referensi:

cnst
sumber
2

Ketika garis miring ditambahkan ke jenkins proksi nginx, Anda akan disajikan dengan kesalahan "Tampaknya pengaturan proxy terbalik Anda rusak".

proxy_pass          http://localhost:8080/;

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

Itu harus dibaca

proxy_pass          http://localhost:8080;
Tomdunn
sumber
Saya tidak berpikir bahwa ini adalah apa yang terkait dengan pertanyaan OP atau memecahkan salah satu masalah yang disebutkan.
Cory Robinson