Nginx proxy reverso + reescrita de URL

149

O Nginx está sendo executado na porta 80 e estou usando-o para reverter URLs de proxy com caminho /foopara a porta 3200desta maneira:

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

Isso funciona bem, mas eu tenho um aplicativo na porta 3200, para o qual não quero que a inicial /fooseja enviada. Ou seja - quando eu acessar http://localhost/foo/bar, quero apenas /barser o caminho recebido pelo aplicativo. Então, tentei adicionar esta linha ao bloco de localização acima:

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

Isso causa o redirecionamento 302 (alteração no URL), mas eu quero o 301. O que devo fazer?

jeffreyveon
fonte
Se você tiver algum problema com o caso Grafana, use esta receita: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Respostas:

168

Qualquer redirecionamento para o host local não faz sentido em um sistema remoto (por exemplo, navegador da Web do cliente). Portanto, os sinalizadores de reescrita permanente (301) ou redirecionado (302) não são utilizáveis ​​no seu caso.

Tente seguir a configuração usando uma regra de reescrita transparente:

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

Use curl -ipara testar suas reescritas. Uma mudança muito sutil na regra pode fazer com que o nginx execute um redirecionamento.

Jens Bradler
fonte
1
O caminho URL ainda começa com / foo em meu aplicativo quando faço isso ...
jeffreyveon
Deve haver um problema diferente. Eu reproduzi esse cenário com sucesso, apenas alguns minutos atrás. URL original: http: // development / foo / testme / 1234 - REQUEST_URI de um script PHP em execução em um Apache conectado como back-end de proxy: '/ testme / 1234'
Jens Bradler
9
O regex provavelmente deve ser /foo(.*), caso contrário example.com/foonão será correspondido. (que é provavelmente o que jeffreyveon experiente)
Benno
Isso funciona, mas meu corpo que estou configurando com proxy_set_body está sendo removido.
Justin Thomas
reescreva /(.*) /socket.io/ break; Salve o meu dia no SOCKET.IO
#
124

A correspondência simples de prefixo de local funciona para isso sem usar uma regra de reescrita, desde que você especifique um URI na diretiva proxy_pass:

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

Observe o adicional /no final da proxy_passdiretiva. O NGINX retira o prefixo correspondente /fooe passa o restante para o servidor back-end no URI /. Portanto, http://myserver:80/foo/barserá publicado no back-end em http://localhost:3200/bar.

A partir dos docs nginx sobre proxy_pass :

Se a diretiva proxy_pass for especificada com um URI, quando uma solicitação for passada para o servidor, a parte de uma URI de solicitação normalizada correspondente ao local será substituída por uma URI especificada na diretiva:

DanArl
fonte
12
Funciona para mim do que adicionei / ao local / foo / {
Andrei N
Era exatamente isso que eu estava procurando!
anbiniyar
6
Esta é uma solução muito limpa, eu preferiria que esta seja a resposta canônica para a pergunta.
ralien 29/09/16
2
Demorou muito para perceber a importância de manter ou remover a barra final.
Parvez
5
Isso realmente passará //xyzpara o host, se você fizer isso.
Archimedes Trajano
60

A maneira mais correta e as melhores práticas absolutas são geralmente as seguintes:

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

  • Observe a extrema importância da barra finalproxy_pass , que altera automaticamente a $urivariável para que /foo/o front-end corresponda /ao back-end. Não há necessidade de uma rewritediretiva explícita .

  • Além disso, observe que o rastreamento /no site tambémlocation é bastante importante - sem ele, você corre o risco de ter URLs de aparência estranha no site em um ponto (por exemplo, um trabalho /fooenalém de /foo/en).

    Além disso, o rastreamento /no locationcom proxy_passtambém garante um tratamento especial , conforme a documentação da locationdiretiva, para efetivamente causar um implícito location = /foo {return 301 /foo/;}também.

    Portanto, ao definir locationa barra com a barra à direita, você não apenas garante que URLs com sufixo sem barra como /fooennão sejam válidos, mas também que uma /foobarra sem barra continue a funcionar também.


Documentação de referência:

cnst
fonte
Parece que $argsestão perdidos: http://frontend/foo?bar=bazserão enviados por proxy http://backend/. Observe que args não são a parte url
Vanuan
@Vanuan, você tem certeza disso? Tenho certeza de $argsque ainda deve ser tratado adequadamente se você usar o código acima, pois eles são separados $urie devem ser reunidos novamente, a menos que você esteja usando variáveis ​​explícitas no seu proxy_pass.
CNST
@ Cnst Oh, entendo. Estou usando a variável host. Isso é contra-intuitivo.
Vanuan 4/11
1
@ArchimedesTrajano, você está incorreto, pois há um tratamento especial para /fooredirecionar /foo/, portanto, a menos que você esteja fazendo algo estranho no back-end, até as /foosolicitações ainda funcionarão com o código acima. (Esta é realmente já parte da resposta, BTW.)
CNST
3
Embora essa solução "pareça" vencer em todos esses fóruns, deve-se notar na própria resposta que o Nginx decodificará o URL e passará o URL decodificado para o servidor proxy. Portanto, esta solução não funcionará se o seu URL contiver partes codificadas.
codificação
1

experimentar

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

ou

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
DerGeh
fonte
13
Essa resposta seria boa se você der uma explicação sobre por que ela deve ser configurada como acima.
precisa saber é o seguinte
Isso realmente passará //xyzpara o host, se você fizer isso.
Archimedes Trajano
1

@Terabuck Desculpe por não responder ainda não.

Você não deve usar o host local, pois depende do fato de o aplicativo estar sendo executado em um servidor com um arquivo de hosts. host local é apenas uma tradução padrão para 127.0.0.1. Não há nada indicando que você deve ter esses arquivos de hosts. É muito comum ter um.

Ter uma interface de loopback é outra coisa comum de se depender, mas você ainda depende da interface de loopback na pilha de rede. É um caso raro não ter esses dois. Se você se preocupar com isso. Pelo menos no unix / linux, você tem a opção de soquetes. Isso eliminará a necessidade de a pilha de rede alcançar o host local. Tenha cuidado com essa abordagem, pois existem alguns fatores que serão implementados no sistema operacional host. Como o número de arquivos abertos etc.

Cody Van Lith
fonte