Nginx https menulis ulang ternyata POST menjadi GET

17

Server proxy saya berjalan di ip A dan ini adalah cara orang mengakses layanan web saya. Konfigurasi nginx akan mengarahkan ulang ke mesin virtual di ip B.

Untuk server proxy pada IP A, saya memilikinya di situs saya-tersedia

server {
        listen 443;
        ssl on;
        ssl_certificate nginx.pem;
        ssl_certificate_key nginx.key;

        client_max_body_size 200M;
        server_name localhost 127.0.0.1;
        server_name_in_redirect off;

        location / {
                proxy_pass http://10.10.0.59:80;
                proxy_redirect http://10.10.0.59:80/ /;

                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

}

server {
        listen 80;
        rewrite     ^(.*)   https://$http_host$1 permanent;
        server_name localhost 127.0.0.1;
        server_name_in_redirect off;
        location / {
                proxy_pass http://10.10.0.59:80;
                proxy_redirect http://10.10.0.59:80/ /;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

Itu proxy_redirectdiambil dari bagaimana saya mendapatkan nginx untuk meneruskan permintaan HTTP POST melalui penulisan ulang?

Segala sesuatu yang mengenai IP publik akan mencapai 443 karena penulisan ulang. Secara internal, kami meneruskan ke 80 pada mesin virtual.

Tetapi ketika saya menjalankan skrip python seperti yang di bawah ini untuk menguji konfigurasi kami

import requests

data = {'username': '....', 'password': '.....'}
url = 'http://IP_A/api/service/signup'

res  = requests.post(url, data=data, verify=False)
print res
print res.json
print res.status_code
print res.headers

Saya mendapatkan 405 Method Not Allowed. Di nginx kami menemukan bahwa ketika mengenai server internal, nginx internal mendapatkan GETpermintaan, meskipun di header asli kami melakukan POST(ini ditunjukkan dalam skrip Python).

Jadi sepertinya menulis ulang memiliki masalah. Adakah yang tahu bagaimana cara memperbaikinya? Ketika saya berkomentar menulis ulang, pasti mencapai 80, dan sudah selesai. Karena menulis ulang dapat berbicara dengan server internal kami, jadi menulis ulang itu sendiri tidak memiliki masalah. Hanya saja penulisan ulang jatuh POSTke GET.

Terima kasih!

(Ini juga akan ditanyakan di forum Nginx karena ini adalah pemblokir kritis ...)

CppLearner
sumber

Jawaban:

8

Ini bukan Nginx, ini browser Anda.

Catatan dari RFC2616:

RFC 1945 dan RFC 2068 menentukan bahwa klien tidak diperbolehkan mengubah metode berdasarkan permintaan yang dialihkan. Namun, sebagian besar implementasi agen pengguna yang ada memperlakukan 302 seolah-olah itu adalah respons 303, melakukan GET di Lokasi [..]

Ini berlaku untuk semua browser populer dan tidak ada yang dapat Anda lakukan untuk itu.

c2h5oh
sumber
@ c2h50h Saya mengerti bahwa spec HTTP menyatakan sesuatu yang mirip seperti itu. Tapi apa yang bisa saya lakukan di Nginx? Maksud saya ini adalah setup sepele mana orang maju 443 ke port internal 80, tetapi mereka masih bisa melakukan PUT, POST, DELETE, GET. Dalam pengaturan saya sebelumnya, saya tidak memiliki proksi tambahan ini di bagian depan yang melayani kerumunan. Saya memiliki konfigurasi yang sama pada server internal yang sama (server pengujian kami). Itu bekerja dengan baik.
CppLearner
Tidak ada. Ini sisi klien 100%. Jika server web, server web mana pun, mengembalikan pengalihan 301 atau 302 maka browser, di sisi klien, akan menggantikan semua jenis permintaan GET. Tidak ada konfigurasi sisi server atau header http apa pun yang dikembalikan tidak akan mengubahnya. Itu seperti itu karena alasan historis (browser awal berperilaku seperti itu karena kesalahpahaman dan itu menjadi standar de facto).
c2h5oh
Nah untuk yang satu ini sebenarnya bukan browser. Yah Anda bisa mengatakan itu browser karena menggunakan protokol HTTP .. mungkin .. itu baik-baik saja. Tetapi sekali lagi, sepertinya ini hanya terjadi jika saya melakukan lebih dari konfigurasi dua-lapisan ini. Jika saya meletakkan konfigurasi yang sama langsung pada mesin internal, dan menjalankan tes di sana, itu tidak akan mengeluh. Tetapi sekali lagi, bagaimana orang melakukannya dalam produksi mereka? Saya akan berasumsi beberapa orang melakukan hal yang serupa, membalikkan 443 ke beberapa VM yang mungkin hanya menjalankan 80 saja. Jika ada latihan yang lebih baik, saya ingin mempelajarinya dan mendengarnya.
CppLearner
1
Dengan browser saya maksudkan klien HTTP dan dengan semua klien populer POSTakan menjadi GETjika 301 atau 302 diarahkan. POST akan tetap POST pada redirect proxy, tetapi tidak pada penulisan ulang.
c2h5oh
1
RFC2616 lagi: If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.Jadi sebagian besar browser akan memunculkan pesan peringatan, seperti untuk klien HTTP lain, saya bahkan tidak bisa menebak apa perilaku mereka.
c2h5oh
1

Saya menemukan POST /api/brandsedang berubah menjadi GET /api/brandkarena aplikasi web yang saya gunakan ( flask-restful) membuat permintaan "tidak valid". Jika saya menggunakan POST /api/brand/(perhatikan trailing /), itu berhasil.

gaozhidf
sumber
Saya menggunakan Postman untuk menguji login rest-auth Django dan melihat masalah yang sama dijelaskan. Kuncinya adalah bahwa saya telah mengabaikan trailing '/' dalam permintaan POST.
Steve L