Nginx membalikkan proxy + penulisan ulang URL

149

Nginx berjalan pada port 80, dan saya menggunakannya untuk membalikkan URL proxy dengan jalur /fooke port 3200dengan cara ini:

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

Ini berfungsi dengan baik, tetapi saya memiliki aplikasi pada port 3200, di mana saya tidak ingin inisial /foodikirim. Yaitu - ketika saya mengakses http://localhost/foo/bar, saya hanya ingin /barmenjadi jalur yang diterima oleh aplikasi. Jadi saya mencoba menambahkan baris ini ke blok lokasi di atas:

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

Ini menyebabkan 302 redirect (perubahan URL), tetapi saya ingin 301. Apa yang harus saya lakukan?

jeffreyveon
sumber
Jika Anda memiliki masalah dengan kasing Grafana, Anda harus menggunakan resep ini: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Jawaban:

168

Redirect apa pun ke localhost tidak masuk akal dari sistem jarak jauh (misalnya browser Web klien). Jadi flag penulisan ulang permanen (301) atau redirect (302) tidak dapat digunakan dalam kasus Anda.

Silakan coba pengaturan berikut menggunakan aturan penulisan ulang transparan:

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

Gunakan curl -iuntuk menguji penulisan ulang Anda. Perubahan yang sangat halus pada aturan dapat menyebabkan nginx melakukan redirect.

Jens Bradler
sumber
1
Jalur URL masih dimulai dengan / foo di aplikasi saya ketika saya melakukan itu ...
jeffreyveon
Pasti ada masalah yang berbeda. Saya berhasil mereproduksi skenario ini, hanya beberapa menit yang lalu. URL asli: http: // development / foo / testme / 1234 - REQUEST_URI dari skrip PHP yang berjalan di Apache yang terhubung sebagai proxy back-end: '/ testme / 1234'
Jens Bradler
9
Regex mungkin harus /foo(.*), jika example.com/footidak , tidak akan cocok. (yang mungkin apa yang dialami jeffreyveon)
Benno
Jenis ini berfungsi, tetapi tubuh saya yang saya setting dengan proxy_set_body sedang dihapus.
Justin Thomas
menulis ulang /(.*) /socket.io/ break; SIMPAN HARI SAYA UNTUK SOCKET.IO
user956584
124

Pencocokan awalan lokasi sederhana berfungsi untuk ini tanpa menggunakan aturan penulisan ulang selama Anda menentukan URI dalam direktif proxy_pass:

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

Perhatikan tambahan /di akhir proxy_passarahan. NGINX akan menghapus awalan yang cocok /foodan meneruskan sisanya ke server backend di URI /. Karenanya, http://myserver:80/foo/barakan dikirim ke backend di http://localhost:3200/bar.

Dari dokumen NGINX di proxy_pass :

Jika direktif proxy_pass ditentukan dengan URI, maka ketika permintaan diteruskan ke server, bagian dari URI permintaan dinormalisasi yang cocok dengan lokasi digantikan oleh URI yang ditentukan dalam direktif:

DanArl
sumber
12
Bekerja untuk saya daripada yang saya tambahkan / ke lokasi / foo / {
Andrei N
Inilah tepatnya yang saya cari!
anbiniyar
6
Ini adalah solusi yang sangat bersih, saya lebih suka ini menjadi jawaban kanonik untuk pertanyaan itu.
ralien
2
Butuh waktu terlalu lama untuk menyadari pentingnya menjaga atau menghapus garis miring.
Parvez
5
Ini sebenarnya akan diteruskan //xyzke host jika Anda melakukannya.
Archimedes Trajano
60

Cara yang paling benar mutlak dan praktik terbaik biasanya sebagai berikut:

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

  • Perhatikan pentingnya trailing slash inproxy_pass , yang secara otomatis mengubah $urivariabel agar yang ada /foo/di front-end sesuai dengan /di backend. Tidak perlu rewritearahan eksplisit .

  • Selain itu, perhatikan bahwa jejak /di dalamlocation juga cukup penting - tanpanya, Anda berisiko memiliki URL yang tampak aneh di situs Anda pada satu titik (mis., /fooenSelain bekerja /foo/en).

    Selain itu, trailing /di locationdengan proxy_passjuga memastikan beberapa penanganan khusus , sesuai dengan dokumentasi dari locationdirektif, untuk secara efektif menyebabkan implisit location = /foo {return 301 /foo/;}juga.

    Jadi, dengan mendefinisikan a locationdengan trailing slash seperti di atas, Anda tidak hanya memastikan bahwa URL sufiks slash-less seperti /fooentidak akan valid, tetapi juga bahwa /footanpa trailing slash akan terus berfungsi juga.


Dokumentasi rujukan:

cnst
sumber
Sepertinya $argshilang: http://frontend/foo?bar=bazakan diproksikan ke http://backend/. Perhatikan bahwa args bukan bagian dari url
Vanuan
@ Vanuan, apakah Anda yakin tentang itu? Saya cukup yakin $argsmasih harus ditangani dengan tepat jika Anda menggunakan kode di atas, karena mereka terpisah dari $uri, dan harus dikumpulkan kembali, kecuali jika Anda menggunakan variabel eksplisit di proxy_pass.
cnst
@cnst Oh, begitu. Saya menggunakan variabel host. Itu berlawanan dengan intuisi.
Vanuan
1
@ArchimedesTrajano, Anda salah, karena ada penanganan khusus untuk /foodialihkan ke /foo/, jadi, kecuali jika Anda melakukan sesuatu yang aneh di backend, bahkan /foopermintaan masih akan bekerja dengan kode di atas. (Ini sebenarnya sudah bagian dari jawabannya, BTW.)
cnst
3
Meskipun solusi ini "tampaknya" menang di semua forum ini, harus dicatat dalam jawaban itu sendiri bahwa Nginx akan URL decode URL dan meneruskan URL yang diterjemahkan ke server proksi. Jadi solusi ini tidak akan berfungsi jika URL Anda membawa URL yang disandikan.
coding
1

mencoba

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

atau

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
DerGeh
sumber
13
Jawaban ini akan bagus jika Anda memberikan penjelasan mengapa harus dikonfigurasi seperti di atas.
masegaloeh
Ini sebenarnya akan diteruskan //xyzke host jika Anda melakukannya.
Archimedes Trajano
1

@Terabuck Maaf karena belum menjawab tidak ada perwakilan.

Anda tidak boleh menggunakan localhost karena Anda bergantung pada kenyataan bahwa aplikasi berjalan pada server dengan file host. host lokal hanya terjemahan default ke 127.0.0.1. Tidak ada yang menyatakan bahwa Anda harus memiliki file host ini. Sangat umum untuk memilikinya.

Memiliki antarmuka loopback sekali lagi adalah hal yang umum untuk bergantung tetapi Anda masih bergantung pada antarmuka loopback pada tumpukan jaringan. Ini adalah kasus yang langka untuk tidak memiliki keduanya. Jika Anda pernah khawatir tentang ini. Setidaknya pada unix / linux Anda memiliki opsi untuk soket. Ini akan menghilangkan kebutuhan untuk tumpukan jaringan untuk mencapai localhost. Berhati-hatilah dengan pendekatan ini karena ada beberapa faktor yang akan terjadi pada OS host. Seperti jumlah file yang terbuka dll.

Cody Van Lith
sumber