Przekierowanie Nginx przez proxy, przepisz i zachowaj adres URL

71

W Nginx próbowaliśmy przekierować adres URL w następujący sposób:

http://example.com/some/path -> http://192.168.1.24

gdzie użytkownik nadal widzi oryginalny adres URL w przeglądarce. Gdy użytkownik zostanie przekierowany, powiedz, że kliknął link do /section/index.html, chcielibyśmy, aby zgłoszenie to prowadziło do przekierowania

http://example.com/some/path/section/index.html -> http://192.168.1.24/section/index.html

i ponownie zachowaj oryginalny adres URL.

Nasze próby obejmowały różne rozwiązania wykorzystujące proxy i reguły przepisywania, a poniżej pokazuje konfigurację, która zbliżyła nas do rozwiązania (zauważ, że jest to konfiguracja serwera WWW dla example.comserwera WWW). Nadal istnieją jednak dwa problemy:

  • Nie wykonuje poprawnie przepisywania, ponieważ adres URL żądania otrzymany przez serwer WWW http://192.168.1.24zawiera, /some/patha zatem nie obsługuje wymaganej strony.
  • Po najechaniu kursorem na link po wyświetleniu strony /some/pathbrakuje adresu URL

    server {
        listen          80;
        server_name     www.example.com;
    
        location /some/path/ {
            proxy_pass http://192.168.1.24;
            proxy_redirect http://www.example.com/some/path http://192.168.1.24;
            proxy_set_header Host $host;
        }
    
        location / {
            index index.html;
            root  /var/www/example.com/htdocs;
        }
    }
    

Szukamy rozwiązania, które wymaga jedynie zmiany konfiguracji serwera WWW example.com. Jesteśmy w stanie zmienić konfigurację na 192.168.1.24(także Nginx), jednak chcemy tego uniknąć, ponieważ będziemy musieli powtórzyć tę konfigurację dla setek różnych serwerów, przez których dostęp jest proxy example.com.

robjohncox
źródło

Odpowiedzi:

59

Po pierwsze, nie powinieneś używać rootdyrektywy wewnątrz bloku lokalizacji, jest to zła praktyka. W tym przypadku nie ma to jednak znaczenia.

Spróbuj dodać drugi blok lokalizacji:

location ~ /some/path/(?<section>.+)/index.html {
    proxy_pass http://192.168.1.24/$section/index.html;
    proxy_set_header Host $host;
}

Przechwytuje część po / some / path / i przed index.html do zmiennej $ section, która jest następnie używana do ustawienia miejsca docelowego proxy_pass. Możesz sprecyzować wyrażenie regularne, jeśli potrzebujesz.

Tero Kilkanen
źródło
1
Przepraszamy za spóźnioną odpowiedź - jest to tak blisko osiągnięcia tego, czego szukamy. Jedyną wadą jest to, że po wyświetleniu strony docelowej adresy URL linków w przeglądarce nie zawierają w nich „/ some / path /”, co oznacza, że ​​nie działają, jeśli użytkownik je kliknie. Jeśli uda nam się wypracować sposób na przezwyciężenie tego, zaktualizuję i zaakceptuję tę odpowiedź, ponieważ jest ona już prawie gotowa.
robjohncox
8
Łącza, które widzi przeglądarka, są generowane przez oprogramowanie działające na serwerze 192.168.1.24. Powinieneś zmodyfikować to oprogramowanie, aby osiągnąć to, co chcesz.
Tero Kilkanen
nie jestem pewien, że przestrzegam twojego ostrzeżenia o rootie w bloku lokalizacji. czytanie dokumentacji nginx to właściwy sposób na robienie różnych rzeczy. ostrzegają tylko przed złymi praktykami, że nie mają domyślnego katalogu głównego poza wszystkimi lokalizacjami. nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/…
guy mograbi
Cóż, łatwiej jest zastosować ogólną zasadę, aby nie używać rootwewnątrz locationbloku, wtedy nie uzyskasz żadnego nieoczekiwanego zachowania dla domyślnych lokalizacji. Tylko jeśli musisz zmienić ustawienie domyślne rootdla każdej lokalizacji, możesz z niego korzystać.
Tero Kilkanen,
1
Co rozumiesz przez otrzymanie nazwy $ host jako nazwy ? Jaki jest dokładnie wysłany nagłówek HTTP i co dokładnie ma zostać wysłane?
Tero Kilkanen,
65

Powinieneś użyć części URI w proxy_passdyrektywie. Pomieszałeś też argumenty dotyczące porządku proxy_redirectdyrektywy i prawdopodobnie wcale jej nie potrzebujesz. Nginx ma rozsądne domyślne ustawienie tej dyrektywy.

W takim przypadku Twój locationblok może być naprawdę prosty:

location /some/path/ {
    proxy_pass http://192.168.1.24/;
    # note this slash  -----------^
    proxy_set_header Host $host;
}
Alexey Ten
źródło
1
Przepraszamy za spóźnioną odpowiedź - wypróbowałem to i niestety nie działa w naszym przypadku użycia. Problem polega na tym, że gdy żądanie jest przesyłane na serwerze docelowym, /some/path/część adresu URL jest zachowywana w żądaniu, które nie jest prawidłowym adresem URL (musimy go również przepisać, aby go usunąć).
robjohncox
@robjohncox co dokładnie próbowałeś?
Alexey Ten
9
cięcie zrobiło dla mnie lewę. Teraz mojadomena.com/some/path/* jest poprawnie proxy pod 192.168.1.24/*, a nie 192.168.1.24/some/path/*
Vadimo,
7
Czy mogę wypowiedzieć się w komentarzu „# note this slash” w tej odpowiedzi? Trzy okrzyki za ten komentarz!
8one6
Nie jestem pewien, jak to działa dla was wszystkich. To właśnie staram się osiągnąć. Jednak gdy użytkownik kliknie link, który przekieruje np. Na 192.168.1.24/login w serwisie lokalnym, zostanie przekierowany na mydomain.com/login zamiast mydomain.com/some/path/login
mueslo,
4

Możesz użyć poniższej konfiguracji, aby uzyskać 100% płynne mapowanie między /some/path/front- /endem i backendem.

Zauważ, że jest to jedyna jak dotąd odpowiedź, która bezproblemowo zajmowałaby się także generowaniem 404 Not Foundbłędów bezwzględnych ścieżek , pod warunkiem, że Refererprzeglądarka wysyła poprawny nagłówek HTTP , więc wszystkie te gify powinny nadal się ładować bez potrzeby modyfikowania podstawowego kodu HTML (co jest nie tylko drogie, ale także nie jest obsługiwane bez dodatkowych modułów, które nie zostały skompilowane domyślnie).

location /some/path/ {
    proxy_pass http://192.168.1.24/; # note the trailing slash!
}
location / {
    error_page 404 = @404;
    return 404; # this would normally be `try_files` first
}
location @404 {
    add_header Vary Referer; # sadly, no effect on 404
    if ($http_referer ~ ://[^/]*(/some/path|/the/other)/) {
        return 302 $1$uri;
    }
    return 404 "Not Found\n";
}

Można znaleźć kompletny proof-of-concept i minimal-realny produkt uboczny w ramach https://github.com/cnst/StackOverflow.cnst.nginx.conf repozytorium.

Oto przebieg testowy, aby potwierdzić, że wszystkie przypadki krawędzi wydają się działać:

curl -v -H 'Referer: http://example.su/some/path/page.html' localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
> Referer: http://example.su/some/path/page.html
< HTTP/1.1 302 Moved Temporarily
< Location: http://localhost:6586/some/path/and/more.gif
< Vary: Referer

curl -v localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
< HTTP/1.1 404 Not Found

curl -v localhost:6586/some/path/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location -e uri
> GET /some/path/and/more.gif HTTP/1.1
< HTTP/1.1 200 OK
request_uri:    /and/more.gif

PS Jeśli masz wiele różnych ścieżek do zmapowania, to zamiast przeprowadzania porównania wyrażeń regularnych $http_refererw ifobrębie location @404, możesz mapzamiast tego skorzystać z globalnej dyrektywy.

Zauważ też, że końcowe ukośniki zarówno w proxy_pass, jak i w locationnim zawarte, są dość ważne, jak na pokrewną odpowiedź .

Bibliografia:

cnst
źródło
2

Po dodaniu tego ukośnika do jenkins z serwerem proxy nginx pojawia się błąd „Wygląda na to, że konfiguracja odwrotnego proxy jest zepsuta”.

proxy_pass          http://localhost:8080/;

Remove this -----------------------------^

Powinien przeczytać

proxy_pass          http://localhost:8080;
tomdunn
źródło
Nie sądzę, żeby to było związane z pytaniem PO, ani nie rozwiązało żadnego z wymienionych problemów.
Cory Robinson