A reescrita Nginx https transforma POST em GET

17

Meu servidor proxy roda no ip A e é assim que as pessoas acessam meu serviço web. A configuração do nginx será redirecionada para uma máquina virtual no ip B.

Para o servidor proxy no IP A, eu tenho isso nos meus sites - disponíveis

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

O proxy_redirectfoi tirado de como faço para obter nginx para encaminhar solicitações HTTP POST via reescrita?

Tudo o que atingir o IP público atingirá 443 por causa da reescrita. Internamente, estamos encaminhando para 80 na máquina virtual.

Mas quando eu executo um script python como o abaixo para testar nossa configuração

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

Estou recebendo um 405 Method Not Allowed. No nginx, descobrimos que, quando ele atingia o servidor interno, o nginx interno estava recebendo uma GETsolicitação, mesmo que no cabeçalho original que fizemos POST(isso foi mostrado no script Python).

Parece que reescrever tem problema. Alguma idéia de como consertar isso? Quando comentei a reescrita, ela atinge 80, com certeza, e passou. Como a reescrita foi capaz de falar com o servidor interno, a reescrita em si não tem problema. É apenas a reescrita caiu POSTpara GET.

Obrigado!

(Isso também será solicitado no fórum do Nginx porque este é um bloqueador crítico ...)

CppLearner
fonte

Respostas:

8

Não é o Nginx, é o seu navegador.

Nota do RFC2616:

RFC 1945 e RFC 2068 especificam que o cliente não tem permissão para alterar o método na solicitação redirecionada. No entanto, a maioria das implementações de agentes de usuários existentes tratam 302 como se fosse uma resposta 303, executando um GET no local [..]

Isso é verdade para todos os navegadores populares e não há nada que você possa fazer sobre isso.

c2h5oh
fonte
@ c2h50h Entendo que a especificação HTTP declarou algo semelhante assim. Mas o que posso fazer no Nginx? Quero dizer isso é uma configuração trivial onde as pessoas para a frente 443 para uma porta interna 80, mas eles ainda podem fazer PUT, POST, DELETE, GET. Na minha configuração anterior, eu não tinha esse proxy extra na frente, servindo a multidão. Eu tinha a mesma configuração no mesmo servidor interno (nosso servidor de teste). Isso funciona bem.
CppLearner,
Nada. É 100% do lado do cliente. Se um servidor da Web, qualquer servidor da Web, retornar um redirecionamento 301 ou 302, o navegador, no lado do cliente, substituirá qualquer tipo de solicitação para GET. Nenhuma configuração do lado do servidor ou qualquer cabeçalho http retornado não mudará isso. É assim por razões históricas (os primeiros navegadores se comportaram dessa maneira devido a mal-entendidos e se tornaram um padrão de fato).
C2h5oh 02/10/12
Bem, para um, isso não é realmente um navegador. Bem, você pode dizer que é navegador, porque ele usa protocolo HTTP ... ok .. tudo bem. Mas, novamente, parece que isso só acontece se eu estivesse fazendo essa configuração de duas camadas. Se eu colocasse a mesma configuração diretamente na máquina interna e executando o teste lá, ela não reclamará. Mas, novamente, como as pessoas fazem isso em sua produção? Eu diria que algumas pessoas estão fazendo algo semelhante, inverta 443 para alguma VM que pode estar executando apenas 80. Se houver uma prática melhor, eu gostaria de aprender e ouvir sobre isso.
CppLearner
1
Por navegador, quis dizer cliente HTTP e com todos os clientes populares POST, GETse ele for redirecionado 301 ou 302. O POST permanecerá no redirecionamento de proxy, mas não na reescrita.
C2h5oh 02/10/12
1
RFC2616 novamente: 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.assim, a maioria dos navegadores exibirá uma mensagem de aviso, pois para outros clientes HTTP, eu nem consigo imaginar qual será o comportamento deles.
C2h5oh 2/10/12
1

Eu descobri que POST /api/brandestava sendo transformado GET /api/brandporque o aplicativo Web que eu estava usando ( flask-restful) estava fazendo uma solicitação "inválida". Se eu usei POST /api/brand/(observe a trilha /), foi bem-sucedida.

gaozhidf
fonte
Eu estava usando o Postman para testar o login do django rest-auth e estava vendo o mesmo problema descrito. A chave era que eu havia negligenciado o '/' à direita na solicitação POST.
Steve L