Wie schreibe ich URLs in einer Proxy-Antwort in NGINX neu?

86

Ich bin es gewohnt, Apache mit mod_proxy_html zu verwenden, und versuche, mit NGINX etwas Ähnliches zu erreichen. Der spezielle Anwendungsfall besteht darin, dass in Tomcat auf Port 8080 auf einem Server im Stammkontext eine Administrator-Benutzeroberfläche ausgeführt wird:

http://localhost:8080/

Ich muss dies auf Port 80 anzeigen, habe aber andere Kontexte auf dem NGINX-Server, der auf diesem Host ausgeführt wird. Daher möchte ich versuchen, auf Folgendes zuzugreifen:

http://localhost:80/admin/

Ich hatte gehofft, dass der folgende supereinfache Serverblock es schaffen würde, aber es ist nicht ganz so:

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

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

Das Problem ist, dass der zurückgegebene Inhalt (HTML) URLs zu Skripten und Stilinformationen enthält, auf die alle im Stammkontext zugegriffen wird. Daher müssen diese URLs neu geschrieben werden, um mit / admin / anstelle von / zu beginnen.

Wie mache ich das in NGINX?

IanG
quelle

Antworten:

121

Wir sollten zuerst die Dokumentation zu proxy_pass sorgfältig und vollständig lesen .

Der an den Upstream-Server übergebene URI wird basierend darauf bestimmt, ob die Direktive "proxy_pass" mit dem URI verwendet wird oder nicht. Ein abschließender Schrägstrich in der Anweisung proxy_pass bedeutet, dass der URI vorhanden und gleich ist /. Der Unsinn eines abschließenden Schrägstrichs bedeutet, dass keine URI vorhanden ist.

Proxy_pass mit URI :

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

Mit dem oben genannten gibt es den folgenden Proxy:

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

Grundsätzlich /some_dir/wird ersetzt durch /, um den Anforderungspfad von /some_dir/some_subdir/some_filenach zu ändern /some_subdir/some_file.

Proxy_pass ohne URI :

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

Mit dem zweiten (kein abschließender Schrägstrich): Der Proxy sieht folgendermaßen aus:

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

Grundsätzlich wird der vollständige ursprüngliche Anforderungspfad ohne Änderungen weitergegeben.


In Ihrem Fall sollten Sie also einfach den abschließenden Schrägstrich fallen lassen, um das zu erhalten, was Sie möchten.


Vorbehalt

Beachten Sie, dass das automatische Umschreiben nur funktioniert, wenn Sie in proxy_pass keine Variablen verwenden. Wenn Sie Variablen verwenden, sollten Sie sich selbst neu schreiben:

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

Es gibt andere Fälle, in denen das Umschreiben nicht funktioniert. Deshalb ist das Lesen der Dokumentation ein Muss.


Bearbeiten

Wenn Sie Ihre Frage noch einmal lesen, habe ich möglicherweise übersehen, dass Sie nur die HTML-Ausgabe bearbeiten möchten.

Dafür können Sie die Direktive sub_filter verwenden . Etwas wie ...

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

Grundsätzlich die Zeichenfolge, die Sie ersetzen möchten, und die Ersatzzeichenfolge

Dayo
quelle
2
Danke, das hilft sehr. Ich denke, sub_filter wird es tun.
IanG
2
Ich bin neugierig, inwieweit Nginx die Ausgabe bereits neu schreibt. Muss es nicht mindestens den Host / Hostnamen in Links neu schreiben? Also zB nichtsub_filter "http://localhost/" "http://localhost/admin/"
ThorSummoner
1
Um ein anderes Umschreiben als text/htmlMimetyp zu ermöglichen, musste ich auch hinzufügen sub_filter_types *;.
Anttikoo
Mit dieser Lösung passiert etwas Seltsames für mich. Ressourcen (* .js, * .css usw. werden abgerufen), aber die Seite kann nicht geladen werden. Ich würde erwarten http://your_server/admin/, http://your_serverwährend des proxy_pass aufgelöst zu werden, aber dies ist nicht der Fall und ich erhalte einen Fehler react-router /admin/ location did not match any routesin meiner Anwendung, da meine Anwendung nichts über '/ admin' weiß.
Prachi
Möglicherweise müssen Sie auch eine proxy_redirectDirektive hinzufügen, damit der Locationvon der Antwort gesendete Header entsprechend der URL geändert wird. Schauen Sie sich dieses Tutorial an: cyberciti.biz/faq/…
vivanov
21

Möglicherweise muss vor Backupservern mit Datenkomprimierung vor dem ersten "sub_filter" die folgende Anweisung festgelegt werden:

proxy_set_header Accept-Encoding "";

Andernfalls funktioniert es möglicherweise nicht. Für Ihr Beispiel sieht es so aus:

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

Sie können das folgende Nginx-Konfigurationsbeispiel verwenden:

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
quelle
1
Warum wird das abgelehnt? Irgendein Problem mit dem Code? Scheint mir eine nette und komplexe Lösung zu sein, die einige Vorbehalte beim Proxying einer App löst, die später auftaucht. Ich weiß nicht warum proxy_redirect off;. Auch würde ich hinzufügen proxy_set_header X-Forwarded-Proto $scheme;.
LuH