Menonaktifkan penguraian URL di proksi nginx

21

Ketika saya menjelajah ke URL ini: http://localhost:8080/foo/%5B-%5Dserver ( nc -l 8080) menerimanya apa adanya:

GET /foo/%5B-%5D HTTP/1.1

Namun ketika saya proksi aplikasi ini melalui nginx (1.1.19):

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

Permintaan yang sama dialihkan melalui port nginx diteruskan dengan jalur diterjemahkan:

GET /foo/[-] HTTP/1.1

Tanda kurung siku yang didekode dalam jalur GET menyebabkan kesalahan pada server target ( Status HTTP 400 - Karakter ilegal di jalur ... ) saat tiba tanpa diloloskan.

Apakah ada cara untuk menonaktifkan decoding URL atau menyandikannya kembali sehingga server target mendapatkan jalur yang sama persis ketika dialihkan melalui nginx? Beberapa aturan penulisan ulang URL yang pintar?

Tomasz Nurkiewicz
sumber
Bug yang dilaporkan ke nginx: trac.nginx.org/nginx/ticket/262
Tomasz Nurkiewicz

Jawaban:

19

Mengutip Valentin V. Bartenev (siapa yang harus mendapatkan kredit penuh untuk jawaban ini):

Kutipan dari dokumentasi :

  • Jika proxy_pass ditentukan dengan URI , saat meneruskan permintaan ke server, bagian dari URI permintaan dinormalisasi yang cocok dengan lokasi digantikan oleh URI yang ditentukan dalam arahan

  • Jika proxy_passditentukan tanpa URI , permintaan URI diteruskan ke server dalam bentuk yang sama seperti yang dikirim oleh klien saat memproses permintaan asli

Konfigurasi yang benar dalam kasus Anda adalah:

location /foo {
   proxy_pass http://localhost:8080;
}
Tomasz Nurkiewicz
sumber
8
Aku harus mengubah http://localhost:8080/ke http://localhost:8080dalam kasus orang memiliki situasi yang sama seperti yang saya lakukan.
herrtim
4
Mengapa Nginx memecahkan kode URI sebelum meneruskannya ke server backend? Bukankah lebih masuk akal jika URI tidak tersentuh?
platypus
@platypus, tidak tersentuh, sampai Anda secara eksplisit mulai melakukan pergantian
cnst
2

Perhatikan bahwa penguraian URL, umumnya dikenal sebagai $uri"normalisasi" dalam dokumentasi nginx, terjadi sebelum backend IFF:

  • baik URI ditentukan dalam proxy_passdirinya sendiri, bahkan jika hanya garis miringnya dengan sendirinya,

  • atau, URI diubah selama pemrosesan, misalnya melalui rewrite.


Kedua kondisi secara eksplisit didokumentasikan di http://nginx.org/r/proxy_pass (penekanan milikku):

  • Jika proxy_passarahan ditentukan dengan URI , maka ketika permintaan diteruskan ke server, bagian dari URI permintaan dinormalisasi yang cocok dengan lokasi digantikan oleh URI yang ditentukan dalam arahan

  • Jika proxy_passditentukan tanpa URI , permintaan URI diteruskan ke server dalam bentuk yang sama seperti yang dikirim oleh klien ketika permintaan asli diproses, atau permintaan normalisasi penuh URI diteruskan saat memproses URI yang diubah


Solusinya adalah dengan menghilangkan URI seperti dalam kasus OPs, atau, memang, menggunakan rewriteaturan pintar :

# map `/foo` to `/foo`:
location /foo {
    proxy_pass  http://localhost:8080;  # no URI -- not even just a slash
}

# map `/foo` to `/bar`:
location /foo {
    rewrite  ^  $request_uri;            # get original URI
    rewrite  ^/foo(/.*)  /bar$1  break;  # drop /foo, put /bar
    return 400;   # if the second rewrite won't match
    proxy_pass    http://localhost:8080$uri;
}

Anda dapat melihatnya langsung di jawaban Stack Overflow terkait , termasuk grup kontrol.

cnst
sumber
Dokumentasinya membingungkan di sini. Kedua bentuk mengandung URI. Ini adalah komponen jalur yang ada di satu dan hilang di yang lain.
Michael Hampton
@MichaelHampton, saya tidak setuju - PATH umumnya disebut URI, jadi, yang tanpa jalur, tidak mengandung URI.
cnst
Jalur relatif saja juga bisa menjadi URL yang valid, tentu saja. Intinya, sisanya juga merupakan URI yang valid (misalnya http://localhost:8080). Jika Anda tidak setuju, Anda dapat melakukannya dengan penulis RFC 3986.
Michael Hampton
@MichaelHampton Sayangnya, skema dan jalur wajib untuk menjadi URI, otoritas, argumen, fragmen adalah opsional
Norman Xu