Nginx proxy inverso + reescritura de URL

149

Nginx se ejecuta en el puerto 80, y lo estoy usando para revertir las URL de proxy con la ruta /fooal puerto de 3200esta manera:

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

Esto funciona bien, pero tengo una aplicación en el puerto 3200, para la cual no quiero /fooque se envíe la inicial . Es decir, cuando accedo http://localhost/foo/bar, solo quiero /barser la ruta recibida por la aplicación. Así que intenté agregar esta línea al bloque de ubicación anterior:

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

Esto provoca la redirección 302 (cambio en la URL), pero quiero 301. ¿Qué debo hacer?

jeffreyveon
fuente
Si tiene algún problema con el caso de Grafana, debe usar esta receta: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Respuestas:

168

Cualquier redirección a localhost no tiene sentido desde un sistema remoto (por ejemplo, el navegador web del cliente). Por lo tanto, los indicadores de reescritura permanente (301) o redireccionamiento (302) no se pueden utilizar en su caso.

Intente la siguiente configuración con una regla de reescritura transparente:

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

Úselo curl -ipara probar sus reescrituras. Un cambio muy sutil a la regla puede hacer que nginx realice una redirección.

Jens Bradler
fuente
1
La ruta URL todavía comienza con / foo en mi aplicación cuando hago eso ...
jeffreyveon
Debe haber un problema diferente. Reproduje este escenario con éxito, hace solo unos minutos. URL original: http: // development / foo / testme / 1234 - REQUEST_URI de un script PHP que se ejecuta en un Apache conectado como proxy back-end: '/ testme / 1234'
Jens Bradler
99
La expresión regular probablemente debería ser /foo(.*), de lo contrario example.com/foono se igualará. (que es probablemente lo que experimentó jeffreyveon)
Benno
Este tipo de trabajo funciona, pero mi cuerpo que estoy configurando con proxy_set_body se está eliminando.
Justin Thomas
reescribir /(.*) /socket.io/ break; GUARDE MI DÍA PARA SOCKET.IO
user956584
124

La coincidencia de prefijos de ubicación simple funciona para esto sin usar una regla de reescritura siempre que especifique un URI en la directiva proxy_pass:

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

Observe el adicional /al final de la proxy_passdirectiva. NGINX eliminará el prefijo coincidente /fooy pasará el resto al servidor de fondo en el URI /. Por lo tanto, http://myserver:80/foo/barpublicaremos en el backend en http://localhost:3200/bar.

De los documentos de NGINX en proxy_pass :

Si la directiva proxy_pass se especifica con un URI, cuando se pasa una solicitud al servidor, la parte de un URI de solicitud normalizada que coincide con la ubicación se reemplaza por un URI especificado en la directiva:

DanArl
fuente
12
Funciona para mí de lo que he agregado / a la ubicación / foo / {
Andrei N
¡Esto era precisamente lo que estaba buscando!
anbiniyar
66
Esta es una solución muy limpia, preferiría que esta sea la respuesta canónica a la pregunta.
ralien
2
Tardó demasiado en darse cuenta de la importancia de mantener o eliminar la barra inclinada final.
Parvez
55
Esto realmente pasará //xyzal host si haces eso.
Arquímedes Trajano
60

La forma más correcta y la mejor práctica suele ser la siguiente:

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

  • Tenga en cuenta la gran importancia de la barra diagonal finalproxy_pass , que altera automáticamente la $urivariable para que /foo/el front-end se corresponda con /el back-end. No hay necesidad de una rewritedirectiva explícita .

  • Además, tenga en cuenta que el seguimiento /en el tambiénlocation es bastante importante: sin él, corre el riesgo de tener URL de aspecto extraño en su sitio en un momento (por ejemplo, un trabajo /fooenadicional /foo/en).

    Además, el seguimiento /en el locationcon proxy_passtambién garantiza un manejo especial , según la documentación de la locationdirectiva, para causar también un efecto implícito location = /foo {return 301 /foo/;}.

    Por lo tanto, al definir a locationcon la barra inclinada final como se indicó anteriormente, no solo se asegura de que las URL de sufijo sin barra inclinada como /fooenno sean válidas, sino que también funcionará una /foosin barra inclinada final.


Documentación de referencia:

cnst
fuente
Parece que $argsestán perdidos: http://frontend/foo?bar=bazserán enviados por proxy http://backend/. Tenga en cuenta que los argumentos no son parte de la URL
Vanuan el
@Vanuan, ¿estás seguro de eso? Estoy bastante seguro de $argsque aún debe manejarse de manera adecuada si usa el código anterior, ya que están separados $uriy deben ensamblarse nuevamente, a menos que esté usando variables explícitas en su proxy_pass.
Cnst
@cnst Oh, ya veo. Estoy usando la variable host. Eso es contrario a la intuición.
Vanuan
1
@ArchimedesTrajano, usted es incorrecto, ya que hay un manejo especial para /fooredirigir a /foo/, por lo tanto, a menos que esté haciendo algo extraño en el backend, incluso las /foosolicitudes seguirán funcionando con el código anterior. (Esto en realidad ya es parte de la respuesta, por cierto)
Cnst
3
Si bien esta solución "parece" ganar en todos estos foros, debe tenerse en cuenta en la propia respuesta que Nginx decodificará la URL y pasará la URL decodificada al servidor proxy. Por lo tanto, esta solución no funcionará si su URL contiene partes codificadas con URL.
codificación
1

tratar

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

o

location ^~ /foo {
    proxy_pass http://localhost:3200/;
    ....
DerGeh
fuente
13
Esta respuesta sería buena si da una explicación de por qué debe configurarse como anteriormente.
masegaloeh
Esto realmente pasará //xyzal host si haces eso.
Arquímedes Trajano
1

@Terabuck Lo siento por no responder aún no hay representante.

No debe usar localhost porque depende del hecho de que la aplicación se ejecuta en un servidor con un archivo de hosts. El host local es solo una traducción predeterminada a 127.0.0.1. No hay nada que indique que debe tener estos archivos de host. Es muy común tener uno.

Tener una interfaz de bucle invertido es otra cosa común de la que depender, pero aún depende de la interfaz de bucle invertido en la pila de red. Es un caso raro no tener estos dos. Si alguna vez te preocupas por esto. Al menos en unix / linux tienes la opción de sockets. Esto eliminará la necesidad de que la pila de red llegue al host local. Tenga cuidado con este enfoque, ya que hay algunos factores que se aplicarán en el sistema operativo host. Como el número de archivos abiertos, etc.

Cody Van Lith
fuente