Nginx HTTPS переписать превращает POST в GET

17

Мой прокси-сервер работает на ip A, и именно так люди получают доступ к моему веб-сервису. Конфигурация nginx будет перенаправлять на виртуальную машину на ip B.

Для прокси-сервера на IP A, у меня это на моих сайтах доступно

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

Это proxy_redirectбыло взято из того, как мне получить nginx для пересылки запросов HTTP POST через перезапись?

Все, что попадает на общедоступный IP-адрес, попадет на 443 из-за перезаписи. Внутренне мы пересылаем до 80 на виртуальной машине.

Но когда я запускаю скрипт python, такой как приведенный ниже, чтобы проверить нашу конфигурацию

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

Я получаю 405 Method Not Allowed. В nginx мы обнаружили, что когда он попадает на внутренний сервер, внутренний nginx получает GETзапрос, хотя в исходном заголовке мы сделали POST(это было показано в скрипте Python).

Таким образом, кажется, что переписать есть проблема. Есть идеи, как это исправить? Когда я прокомментировал переписывание, оно наверняка достигнет 80, и оно прошло. Поскольку перезапись была в состоянии общаться с нашим внутренним сервером, поэтому перезапись сама по себе не имеет проблем. Это просто переписано POSTна GET.

Спасибо!

(Это также будет задано на форуме Nginx, потому что это критический блокировщик ...)

CppLearner
источник

Ответы:

8

Это не Nginx, это ваш браузер.

Примечание от RFC2616:

RFC 1945 и RFC 2068 указывают, что клиенту не разрешено изменять метод в перенаправленном запросе. Однако большинство существующих реализаций пользовательского агента обрабатывают 302 так, как если бы это был ответ 303, выполняя GET для Location [..]

Это верно для всех популярных браузеров, и вы ничего не можете с этим поделать.

C2H5OH
источник
@ c2h50h Я понимаю, что в спецификации HTTP указано нечто подобное. Но что я могу сделать в Nginx? Я имею в виду , что это тривиальная установка , где люди вперед 443 к внутреннему 80 порта, но они все еще могут сделать PUT, POST, DELETE, GET. В моей предыдущей настройке у меня не было этого дополнительного прокси на фронте, обслуживающего толпу. У меня была такая же конфигурация на том же внутреннем сервере (наш тестовый сервер). Это прекрасно работает.
CppLearner
Ничего. Это на 100% клиентская сторона. Если веб-сервер, любой веб-сервер, возвращает перенаправление 301 или 302, то браузер на стороне клиента заменит любой тип запроса GET. Никакая конфигурация на стороне сервера или любые возвращенные заголовки http не изменят это. По историческим причинам это так (ранние браузеры вели себя так из-за недопонимания, и это стало стандартом де-факто).
c2h5oh
Ну, для одного это не совсем браузер. Ну, вы можете сказать, что это браузер, потому что он использует протокол HTTP ... ладно ... это нормально. Но опять же, похоже, что это происходит только в том случае, если бы я делал эту двухслойную конфигурацию. Если бы я поставил такую ​​же конфигурацию прямо на внутреннюю машину и запустил там тест, он не будет жаловаться. Но опять же, как люди делают это в своем производстве? Я бы предположил, что некоторые люди делают то же самое, поменяв 443 на виртуальную машину, которая может работать только на 80. Если есть лучшая практика, я хотел бы изучить это и услышать об этом.
CppLearner
1
Под браузером я имел в виду HTTP-клиента и со всеми популярными клиентами POSTстанет, GETесли он 301 или 302 перенаправлен. POST останется POST при перенаправлении прокси, но не при перезаписи.
c2h5oh
1
Снова RFC2616: 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.так что большинство браузеров выдают предупреждающее сообщение, что касается других HTTP-клиентов, я даже не могу догадаться, каково их поведение.
c2h5oh
1

Я обнаружил, POST /api/brandчто превращается в, GET /api/brandпотому что веб-приложение, которое я использовал ( flask-restful), делало «недействительный» запрос. Если я использовал POST /api/brand/(обратите внимание на трейлинг /), это было успешно.

gaozhidf
источник
Я использовал Postman для проверки входа в django rest-auth и видел ту же проблему, описанную выше. Ключ был в том, что я пропустил завершающий символ '/' в запросе POST.
Стив Л