¿Cómo reescribo las URL en una respuesta de proxy en NGINX?

86

Estoy acostumbrado a usar Apache con mod_proxy_html y estoy tratando de lograr algo similar con NGINX. El caso de uso específico es que tengo una interfaz de usuario de administración ejecutándose en Tomcat en el puerto 8080 en un servidor en el contexto raíz:

http://localhost:8080/

Necesito mostrar esto en el puerto 80, pero tengo otros contextos en el servidor NGINX ejecutándose en este host, así que quiero intentar acceder a esto en:

http://localhost:80/admin/

Esperaba que el siguiente bloque de servidor súper simple lo hiciera, pero no es así:

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

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

El problema es que el contenido devuelto (html) contiene URL a scripts e información de estilo a la que se accede en el contexto raíz, por lo que necesito reescribir estas URL para que comiencen con / admin / en lugar de /.

¿Cómo hago esto en NGINX?

IanG
fuente

Respuestas:

121

Primero deberíamos leer la documentación sobre proxy_pass detenida y completamente.

El URI pasado al servidor ascendente se determina en función de si la directiva "proxy_pass" se usa con URI o no. La barra diagonal en la directiva proxy_pass significa que el URI está presente y es igual a /. La ausencia de barra diagonal significa que el URI de sombrero está ausente.

Proxy_pass con URI :

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

Con lo anterior, existe el siguiente proxy:

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

Básicamente, /some_dir/se reemplaza por /para cambiar la ruta de solicitud de /some_dir/some_subdir/some_filea /some_subdir/some_file.

Proxy_pass sin URI :

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

Con el segundo (sin barra al final): el proxy es así:

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

Básicamente, la ruta de solicitud original completa se transmite sin cambios.


Entonces, en su caso, parece que debería soltar la barra diagonal para obtener lo que desea.


Consideración

Tenga en cuenta que la reescritura automática solo funciona si no usa variables en proxy_pass. Si usa variables, debe reescribirlo usted mismo:

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

Hay otros casos en los que la reescritura no funcionaría, por eso la lectura de documentación es imprescindible.


Editar

Al leer su pregunta nuevamente, parece que me he perdido de que solo desea editar la salida html.

Para eso, puede usar la directiva sub_filter . Algo como ...

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

Básicamente, la cadena que desea reemplazar y la cadena de reemplazo

Dayo
fuente
2
Gracias, eso me ayuda mucho. Creo que sub_filter lo hará.
IanG
2
Tengo curiosidad por saber hasta qué punto nginx ya está reescribiendo la salida, ¿no tendría que volver a escribir el host / nombre de host en los enlaces como mínimo? Entonces, por ejemplo, ¿no lo haría ustedsub_filter "http://localhost/" "http://localhost/admin/"
ThorSummoner
1
Para permitir la reescritura que no sea text/htmlmimetype, tuve que agregar también sub_filter_types *;.
anttikoo
Algo extraño me está sucediendo con esta solución. Se están recuperando recursos (* .js, * .css, etc.), pero la página no se carga. Esperaría http://your_server/admin/resolverlo http://your_serverdurante el proxy_pass pero no es así y aparece un error react-router /admin/ location did not match any routesen mi aplicación porque mi aplicación no sabe nada sobre '/ admin'.
Prachi
También es posible que deba agregar una proxy_redirectdirectiva para que el Locationencabezado enviado por la respuesta también se modifique de acuerdo con la URL. Echa un vistazo a este tutorial: cyberciti.biz/faq/...
vivanov
21

Es posible que también necesite que se establezca la siguiente directiva antes del primer "sub_filtro" para servidores backend con compresión de datos:

proxy_set_header Accept-Encoding "";

De lo contrario, es posible que no funcione. Para su ejemplo, se verá así:

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.
fuente
-2

Puede utilizar el siguiente ejemplo de configuración de 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
fuente
1
¿Por qué se vota negativamente? ¿Algún problema con el código? Me parece una solución agradable y compleja, que resuelve algunas advertencias de usar proxy en una aplicación que aparece más adelante. proxy_redirect off;Aunque no estoy seguro de por qué . También agregaría proxy_set_header X-Forwarded-Proto $scheme;.
LuH