Jak przepisać adresy URL w odpowiedzi proxy w NGINX

86

Jestem przyzwyczajony do używania Apache z mod_proxy_html i próbuję osiągnąć coś podobnego z NGINX. Szczególnym przypadkiem użycia jest to, że mam interfejs administratora działający w Tomcat na porcie 8080 na serwerze w kontekście głównym:

http://localhost:8080/

Muszę to pokazać na porcie 80, ale mam inne konteksty na serwerze NGINX działającym na tym hoście, więc chcę spróbować uzyskać dostęp do tego pod adresem:

http://localhost:80/admin/

Miałem nadzieję, że zrobi to następujący super prosty blok serwera, ale nie do końca:

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

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

Problem polega na tym, że zwrócona treść (html) zawiera adresy URL do skryptów i informacje o stylu, do których dostęp jest możliwy w kontekście głównym, więc muszę przepisać te adresy URL, aby zaczynały się od / admin / zamiast /.

Jak to zrobić w NGINX?

IanG
źródło

Odpowiedzi:

121

Najpierw powinniśmy uważnie i w pełni przeczytać dokumentację dotyczącą proxy_pass .

Identyfikator URI przekazany do serwera nadrzędnego jest określany na podstawie tego, czy dyrektywa „proxy_pass” jest używana z identyfikatorem URI, czy nie. Końcowy ukośnik w dyrektywie proxy_pass oznacza, że ​​identyfikator URI jest obecny i równy /. Brak końcowego ukośnika oznacza brak identyfikatora URI.

Proxy_pass z URI :

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

W związku z powyższym istnieje następujący serwer proxy:

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

Zasadniczo /some_dir/zostaje zastąpiony przez, /aby zmienić ścieżkę żądania z /some_dir/some_subdir/some_filena /some_subdir/some_file.

Proxy_pass bez URI :

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

Z drugim (bez końcowego ukośnika): proxy wygląda tak:

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

Zasadniczo pełna oryginalna ścieżka żądania jest przekazywana bez zmian.


Więc w twoim przypadku wydaje się, że powinieneś po prostu porzucić końcowy ukośnik, aby uzyskać to, czego chcesz.


Caveat

Zauważ, że automatyczne przepisywanie działa tylko wtedy, gdy nie używasz zmiennych w proxy_pass. Jeśli używasz zmiennych, powinieneś przepisać siebie:

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

Są inne przypadki, w których przepisywanie nie zadziała, dlatego czytanie dokumentacji jest koniecznością.


Edytować

Czytając ponownie twoje pytanie, wydaje mi się, że mogłem przegapić, że chcesz po prostu edytować wynik HTML.

W tym celu możesz użyć dyrektywy sub_filter . Coś jak ...

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

Zasadniczo ciąg, który chcesz zastąpić, i ciąg zastępujący

Dayo
źródło
2
Dzięki, to bardzo pomaga. Myślę, że sub_filter to zrobi.
IanG,
2
Jestem ciekaw, do jakiego stopnia nginx już przepisuje dane wyjściowe, czy nie musiałby co najmniej ponownie zapisywać hosta / nazwy hosta w linkach? Na przykład, czyż niesub_filter "http://localhost/" "http://localhost/admin/"
ThorSummoner,
1
Aby umożliwić przepisywanie innych niż text/htmltyp MIME, musiałem również dodać sub_filter_types *;.
anttikoo
Z tym rozwiązaniem dzieje się coś dziwnego. Zasoby (* .js, * .css itp. Są pobierane), ale strona nie ładuje się. spodziewałbym http://your_server/admin/się rozwiązania problemu http://your_serverpodczas proxy_pass, ale tak się nie dzieje i otrzymuję błąd react-router /admin/ location did not match any routesw mojej aplikacji, ponieważ moja aplikacja nie wie nic o „/ admin”.
Prachi
Konieczne może być również dodanie proxy_redirectdyrektywy, aby Locationnagłówek wysłany przez odpowiedź był również zmieniany zgodnie z adresem URL. Sprawdź ten samouczek: cyberciti.biz/faq/ ...
vivanov
21

Może być również konieczne ustawienie następującej dyrektywy przed pierwszym „sub_filter” w przypadku serwerów zaplecza z kompresją danych:

proxy_set_header Accept-Encoding "";

W przeciwnym razie może nie działać. Na przykład będzie wyglądać tak:

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.
źródło
-2

Możesz użyć następującego przykładu konfiguracji 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
źródło
1
Dlaczego jest to odrzucane? Masz jakiś problem z kodem? Wydaje mi się to miłym i złożonym rozwiązaniem, rozwiązującym niektóre zastrzeżenia dotyczące proxy aplikacji, które pojawią się później. Nie wiem proxy_redirect off;jednak, dlaczego . Dodałbym też proxy_set_header X-Forwarded-Proto $scheme;.
LuH