Como faço para reescrever URLs em uma resposta de proxy no NGINX

87

Estou acostumado a usar o Apache com mod_proxy_html e estou tentando obter algo semelhante com NGINX. O caso de uso específico é que tenho uma IU do administrador em execução no Tomcat na porta 8080 em um servidor no contexto raiz:

http://localhost:8080/

Preciso expor isso na porta 80, mas tenho outros contextos no servidor NGINX em execução neste host, portanto, quero tentar acessar em:

http://localhost:80/admin/

Eu esperava que o seguinte bloco de servidor super simples fizesse isso, mas não é bem assim:

server {
    listen  80;
    server_name screenly.local.akana.com;

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

O problema é que o conteúdo retornado (html) contém URLs para scripts e informações de estilo que são acessadas no contexto raiz, então preciso reescrever essas URLs para começar com / admin / em vez de /.

Como faço isso no NGINX?

IanG
fonte

Respostas:

128

Devemos primeiro ler a documentação sobre proxy_pass cuidadosa e completamente.

O URI passado para o servidor upstream é determinado com base em se a diretiva "proxy_pass" é usada com o URI ou não. A barra à direita na diretiva proxy_pass significa que o URI está presente e é igual a /. Ausência de barra final significa que o URI do chapéu está ausente.

Proxy_pass com URI :

location /some_dir/ {
    proxy_pass http://some_server/;
}

Com o acima, há o seguinte proxy:

http:// your_server/some_dir/ some_subdir/some_file ->
http:// some_server/          some_subdir/some_file

Basicamente, /some_dir/é substituído por /para alterar o caminho da solicitação de /some_dir/some_subdir/some_filepara /some_subdir/some_file.

Proxy_pass sem URI :

location /some_dir/ {
    proxy_pass http://some_server;
}

Com o segundo (sem barra): o proxy funciona assim:

http:// your_server /some_dir/some_subdir/some_file ->
http:// some_server /some_dir/some_subdir/some_file

Basicamente, o caminho completo da solicitação original é transmitido sem alterações.


Portanto, no seu caso, parece que você deve apenas deixar cair a barra final para obter o que deseja.


Embargo

Observe que a regravação automática só funciona se você não usar variáveis ​​em proxy_pass. Se você usar variáveis, deve reescrever-se:

location /some_dir/ {
  rewrite    /some_dir/(.*) /$1 break;
  proxy_pass $upstream_server;
}

Existem outros casos em que a reescrita não funcionaria, por isso é necessário ler a documentação.


Editar

Lendo sua pergunta novamente, parece que não percebi que você apenas deseja editar a saída html.

Para isso, você pode usar a diretiva sub_filter . Algo como ...

location /admin/ {
    proxy_pass http://localhost:8080/;
    sub_filter "http://your_server/" "http://your_server/admin/";
    sub_filter_once off;
}

Basicamente, a string que você deseja substituir e a string de substituição

Dayo
fonte
2
Obrigado, isso ajuda muito. Eu acho que sub_filter vai fazer isso.
IanG
2
Estou curioso para saber até que ponto o nginx já está reescrevendo a saída, ele não teria que reescrever o host / hostname nos links, no mínimo? Então, por exemplo, você não fariasub_filter "http://localhost/" "http://localhost/admin/"
ThorSummoner
1
Para permitir a reescrita além do text/htmltipo MIME, tive que adicionar também sub_filter_types *;.
Anttikoo
Algo estranho está acontecendo para mim com esta solução. Recursos (* .js, * .css etc estão sendo buscados), mas a página falha ao carregar. Eu esperaria http://your_server/admin/ser resolvido http://your_serverdurante o proxy_pass, mas isso não acontece e recebo um erro react-router /admin/ location did not match any routesno meu aplicativo porque meu aplicativo não sabe nada sobre '/ admin'.
Prachi
Também pode ser necessário adicionar uma proxy_redirectdiretiva para que o Locationcabeçalho enviado pela resposta também seja alterado de acordo com o url. Confira este tutorial: cyberciti.biz/faq/…
vivanov
22

Você também pode precisar que a seguinte diretiva seja definida antes do primeiro "sub_filter" para servidores de back-end com compactação de dados:

proxy_set_header Accept-Encoding "";

Caso contrário, pode não funcionar. Para o seu exemplo, será semelhante a:

location /admin/ {
    proxy_pass http://localhost:8080/;
    proxy_set_header Accept-Encoding "";
    sub_filter "http://your_server/" "http://your_server/admin/";
    sub_filter_once off;
}
Vladimir Sh.
fonte
-1

Você pode usar o seguinte exemplo de configuração nginx:

upstream adminhost {
  server adminhostname:8080;
}

server {
  listen 80;

  location ~ ^/admin/(.*)$ {
    proxy_pass http://adminhost/$1$is_args$args;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $server_name;
  }
}
Alex Elkin
fonte
1
Por que isso foi rejeitado? Algum problema com o código? Parece uma solução agradável e complexa para mim, resolvendo alguns problemas de proxy de um aplicativo que aparecem mais tarde. Não tenho certeza porque proxy_redirect off;embora. Também eu acrescentaria proxy_set_header X-Forwarded-Proto $scheme;.
LuH