Proxy inverso Nginx + riscrittura URL


149

Nginx è in esecuzione sulla porta 80 e la sto usando per invertire gli URL proxy con il percorso /fooper la porta in 3200questo modo:

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

Funziona bene, ma ho un'applicazione sulla porta 3200, per la quale non voglio che l'iniziale /foovenga inviata. Cioè - quando accedo http://localhost/foo/bar, voglio solo /baressere il percorso ricevuto dall'app. Quindi ho provato ad aggiungere questa linea al blocco di posizione sopra:

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

Ciò causa 302 reindirizzamenti (modifica dell'URL), ma voglio 301. Cosa devo fare?


Se hai qualche problema con il caso Grafana dovresti usare queste ricette: docs.grafana.org/installation/behind_proxy/…
mohsen saeedi

Risposte:


168

Qualsiasi reindirizzamento a localhost non ha senso da un sistema remoto (ad esempio il browser Web del client). Quindi i flag di riscrittura permanenti (301) o reindirizzamento (302) non sono utilizzabili nel tuo caso.

Prova a seguire la configurazione usando una regola di riscrittura trasparente:

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

Utilizzare curl -iper testare le riscritture. Una modifica molto sottile alla regola può causare un reindirizzamento a nginx.


1
Il percorso dell'URL inizia ancora con / pippo nella mia app quando lo faccio ...
jeffreyveon,

Ci deve essere un problema diverso. Ho riprodotto questo scenario con successo, pochi minuti fa. URL originale: http: // development / foo / testme / 1234 - REQUEST_URI di uno script PHP in esecuzione su un Apache collegato come back-end proxy: '/ testme / 1234'
Jens Bradler,

9
Il regex dovrebbe probabilmente essere /foo(.*), altrimenti example.com/foonon sarà abbinato. (che è probabilmente quello che sperimentò Jeffreyveon)
Benno

Questo tipo di funzionamento, ma il mio corpo che sto impostando con proxy_set_body viene rimosso.
Justin Thomas,

riscrivi /(.*) /socket.io/ break; SALVA IL MIO GIORNO PER SOCKET.IO
user956584

124

La semplice corrispondenza del prefisso di località funziona per questo senza usare una regola di riscrittura fintanto che si specifica un URI nella direttiva proxy_pass:

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

Si noti il ​​supplemento /alla fine della proxy_passdirettiva. NGINX rimuoverà il prefisso corrispondente /fooe passerà il resto al server back-end nell'URI /. Pertanto, http://myserver:80/foo/barinvieremo al backend all'indirizzo http://localhost:3200/bar.

Dai documenti NGINX su proxy_pass :

Se la direttiva proxy_pass è specificata con un URI, quando una richiesta viene passata al server, la parte di un URI di richiesta normalizzata corrispondente alla posizione viene sostituita da un URI specificato nella direttiva:


12
Funziona per me rispetto a quello che ho aggiunto / a location / foo / {
Andrei N,

Questo era esattamente quello che stavo cercando!
anbiniyar

6
Questa è una soluzione molto pulita, preferirei che questa fosse la risposta canonica alla domanda.
ralien,

2
Ci è voluto troppo tempo per capire l'importanza di mantenere o rimuovere la barra finale.
Parvez,

5
Questo passerà effettivamente //xyzall'host se lo fai.
Archimede Trajano,

60

Il modo più corretto in assoluto e le migliori pratiche sono generalmente le seguenti:

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

  • Nota la terribile importanza della barra finaleproxy_pass , che altera automaticamente la $urivariabile in modo che /foo/il front-end corrisponda /al back-end. Non è necessaria una rewritedirettiva esplicita .

  • Inoltre, tieni presente che anche il trailing /inlocation è abbastanza importante - senza di esso, rischi di avere URL dall'aspetto strano sul tuo sito in un punto (ad esempio, un lavoro /fooenin aggiunta a /foo/en).

    Inoltre, il trascinamento /in locationcon proxy_passassicura anche qualche trattamento speciale , come per la documentazione della locationdirettiva, per causare efficacemente un implicito location = /foo {return 301 /foo/;}pure.

    Quindi, definendo un locationcon la barra finale come sopra, non solo si garantisce che gli URL suffisso senza barra come /fooennon saranno validi, ma anche che anche /foouna barra senza barra continuerà a funzionare.


Documentazione di riferimento:


Sembra che si $argssiano persi: http://frontend/foo?bar=bazverrà delegato http://backend/. Nota che gli arg non fanno parte dell'url
Vanuan,

@Vanuan, ne sei sicuro? Sono abbastanza sicuro che $argsdovresti comunque gestirlo in modo appropriato se usi il codice sopra, poiché sono separati $urie dovresti rimetterlo dentro, a meno che tu non stia usando variabili esplicite nel tuo proxy_pass.
CNST

@cnst Oh, capisco. Sto usando la variabile host. Questo è contro intuitivo.
Vanuan,

1
@ArchimedesTrajano, non sei corretto, dato che c'è una gestione speciale per il /fooreindirizzamento a /foo/, quindi, a meno che tu non stia facendo qualcosa di strano sul back-end, anche le /foorichieste funzioneranno ancora con il codice sopra. (Questo in realtà fa già parte della risposta, BTW.)
primo

3
Mentre questa soluzione "sembra" vincere su tutti questi forum, nella risposta stessa si dovrebbe notare che Nginx decodificherà l'URL e passerà l'URL decodificato al server proxy. Quindi questa soluzione non funzionerà se il tuo URL contiene parti codificate URL.
codifica

1

provare

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

o

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

13
Questa risposta sarebbe buona se dai qualche spiegazione sul perché debba essere configurata come sopra.
Masegaloeh,

Questo passerà effettivamente //xyzall'host se lo fai.
Archimede Trajano,

1

@Terabuck Siamo spiacenti per non aver ancora risposto a nessun rappresentante.

Non dovresti usare localhost perché dipende dal fatto che l'applicazione è in esecuzione su un server con un file hosts. l'host locale è solo una traduzione predefinita in 127.0.0.1. Non c'è nulla che affermi che è necessario disporre di questi file host. È molto comune averne uno.

Avere un'interfaccia di loopback è di nuovo un'altra cosa comune da cui dipendere, ma si è ancora dipendenti dall'interfaccia di loopback sullo stack di rete. È un caso raro non avere questi due. Se mai ti preoccupi di questo. Almeno su unix / linux hai l'opzione per i socket. Ciò eliminerà la necessità per lo stack di rete di raggiungere l'host locale. Prestare attenzione con questo approccio in quanto vi sono alcuni fattori che entreranno in vigore nel sistema operativo host. Come il numero di file aperti ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.