Reverse proxy to serwer na wejściu, który przyjmuje ruch z internetu i przekazuje go do twojej aplikacji. Zanim wpiszesz cokolwiek do nginx, przetestuj aplikację Node bez proxy - z tego samego miejsca w sieci, z którego nginx będzie się z nią łączył. Ten jeden test oszczędza większość późniejszej diagnozy 502 i 504.

Przejdziesz tu przez test aplikacji, minimalną konfigurację, nagłówki, wariant HTTPS do upstreamu, WebSocket, timeouty i sprawdzenie obu ścieżek na koniec. Wszystkie wartości w przykładach to placeholdery - przepisz w ich miejsce dane własnego środowiska.

Po co stawiać proxy przed aplikacją Node?

Aplikacja Node nasłuchuje na własnym porcie i nie powinna wystawiać go światu bezpośrednio. Reverse proxy daje jej jeden punkt wejścia: nginx odbiera żądanie klienta, kieruje je do procesu Node - nazywanego tu upstreamem - i zwraca odpowiedź. Certyfikat TLS trzymasz wtedy w jednym miejscu, nie w każdej usłudze osobno, a kolejna aplikacja na tym samym serwerze to tylko nowy wpis w konfiguracji.

Za tę wygodę płacisz nowym elementem układanki - między klientem a aplikacją pojawia się łącznik, który też może się zepsuć.

Typowy scenariusz tej awarii: aplikacja odpowiada poprawnie na curl z terminala na serwerze, a strona publiczna pokazuje 502. Restarty nic nie dają. Po godzinie okazuje się, że nginx działa w kontenerze i „localhost" znaczy dla niego co innego niż dla terminala na hoście - proxy pukało do własnego kontenera, nie do aplikacji. Oba procesy były zdrowe, zepsuty był adres między nimi. Dlatego pierwszy test wykonuje się z perspektywy nginx, nie z dowolnego okna terminala.

Zanim dotkniesz nginx, sprawdź aplikację bez proxy

Wykonaj test z miejsca, z którego nginx naprawdę widzi upstream: na hoście nginx przy instalacji systemowej albo w jego przestrzeni sieciowej, gdy działa w kontenerze.

curl -i http://ADRES_UPSTREAMU:PORT_APLIKACJI/SCIEZKA_TESTOWA

ADRES_UPSTREAMU to adres lub nazwa osiągalna dla nginx, PORT_APLIKACJI przepisz z konfiguracji procesu Node, a SCIEZKA_TESTOWA ustaw na endpoint, który będziesz potem sprawdzał przez proxy. Jeśli nginx ma łączyć się z aplikacją po HTTPS, użyj tego samego schematu już w tym teście. Opcja -i dokłada do wyniku nagłówki odpowiedzi.

Udany test potwierdza tylko tę jedną ścieżkę dla tego adresu, portu i protokołu - nie dowodzi działania innych endpointów. Zapisz sobie pięć wartości, zanim ruszysz dalej:

  1. protokół (HTTP czy HTTPS),
  2. adres upstreamu,
  3. port aplikacji,
  4. ścieżkę testową,
  5. oczekiwaną klasę odpowiedzi.

Te same wartości muszą pojawić się w konfiguracji i w teście końcowym. Każda rozbieżność między nimi to przyszły błąd 502, którego będziesz szukał po logach.

Minimalna konfiguracja nginx przed aplikacją Node

Wariant HTTP składa się z grupy upstreamu i serwera wejściowego:

upstream NAZWA_UPSTREAMU {
    server ADRES_UPSTREAMU:PORT_APLIKACJI;
}

server {
    listen PORT_WEJSCIOWY;
    server_name DOMENA;

    location / {
        proxy_pass http://NAZWA_UPSTREAMU;
        proxy_http_version 1.1;
        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-Proto $scheme;
    }
}

Grupa upstream wiąże lokalną nazwę z adresem procesu Node - wpisz tu dokładnie ten adres i port, które przeszły test bezpośredni. PORT_WEJSCIOWY i DOMENA opisują listener i vhost widoczny dla klienta. Dyrektywa proxy_pass przekazuje żądanie do upstreamu, a jawne proxy_http_version 1.1 uniezależnia cię od wartości domyślnej, która różni się między wydaniami nginx.

Gdy zmieniasz adres, port albo protokół, ponów test bezpośredni z przestrzeni nginx przed przeładowaniem konfiguracji. Tania minuta z curl kosztuje mniej niż zgadywanie po fakcie, która z dwóch warstw kłamie.

Dlaczego adres, port, protokół i ścieżka muszą się zgadzać?

Test bezpośredni i konfiguracja mają opisywać ten sam cel. http:// w proxy_pass oznacza połączenie bez szyfrowania, https:// połączenie TLS - zmiana schematu bez zgodnej konfiguracji upstreamu zmienia badane połączenie i unieważnia wcześniejszy test.

Grupa upstream przyjmuje domenę, adres IP z opcjonalnym portem albo Unix socket. Pamiętaj, że nazwa usługi w sieci kontenerowej, adres hosta i socket należą do różnych przestrzeni - wybierz wartość osiągalną z procesu nginx, nie z twojego terminala.

Osobna pułapka siedzi w ścieżce. Gdy proxy_pass zawiera URI, nginx zastępuje nim część ścieżki dopasowaną do location - aplikacja dostaje wtedy inny adres, niż wysłał klient. Bez URI żądanie przechodzi w całości. Zanim opublikujesz konfigurację, porównaj ścieżkę z testu bezpośredniego z tą, którą aplikacja faktycznie otrzymuje przez proxy.

Co przekazują nagłówki i czemu im nie ufać?

Bez jawnej konfiguracji nginx ustawia Host na $proxy_host, a Connection na close. Wpis proxy_set_header Host $host przekazuje aplikacji nazwę hosta z żądania klienta - większość frameworków Node tego oczekuje.

Trzy pozostałe nagłówki niosą informacje o kliencie: X-Real-IP jego adres, X-Forwarded-For łańcuch adresów po drodze, X-Forwarded-Proto schemat połączenia. Zmienna $proxy_add_x_forwarded_for dopisuje $remote_addr do otrzymanej wartości X-Forwarded-For, a gdy nagłówka nie było, przyjmuje sam $remote_addr.

Żaden z tych nagłówków nie jest mechanizmem uwierzytelnienia. Klient może wysłać dowolną wartość, więc aplikacja nie powinna wierzyć im w ciemno - to, którym nagłówkom ufać i od którego proxy, ustawia się po stronie frameworka Node, poza tą konfiguracją.

Kiedy upstream mówi po HTTPS?

Ten wariant stosuj tylko wtedy, gdy proxy_pass używa https://:

location / {
    proxy_pass https://NAZWA_UPSTREAMU;
    proxy_http_version 1.1;

    proxy_ssl_server_name on;
    proxy_ssl_name NAZWA_TLS_UPSTREAMU;
    proxy_ssl_verify on;
    proxy_ssl_trusted_certificate /sciezka/do/CA_BUNDLE;

    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-Proto $scheme;
}

Samo https:// szyfruje transport, ale nie sprawdza, z kim rozmawiasz - domyślne proxy_ssl_verify off pomija walidację certyfikatu upstreamu. Dlatego dokładasz cztery dyrektywy: proxy_ssl_server_name on włącza SNI, proxy_ssl_name ustawia nazwę używaną przy SNI i weryfikacji, proxy_ssl_verify on włącza walidację, a proxy_ssl_trusted_certificate wskazuje plik zaufanych CA w formacie PEM. NAZWA_TLS_UPSTREAMU i ścieżka do pliku CA to wartości twojego środowiska.

Jak przepuścić WebSocket?

Nagłówki Upgrade i Connection są typu hop-by-hop, więc nginx nie przekazuje ich do upstreamu automatycznie. Dodaj je wyłącznie w lokalizacji, która obsługuje żądanie zmiany protokołu:

location /SCIEZKA_WEBSOCKET {
    proxy_pass http://NAZWA_UPSTREAMU;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

SCIEZKA_WEBSOCKET zastąp ścieżką używaną przez aplikację. Odpowiedź 101 potwierdza przełączenie protokołu dla tego żądania - nie jest ogólnym testem zdrowia aplikacji. Nie kopiuj tego wariantu do każdej lokalizacji: jeśli aplikacja nie używa WebSocket, te nagłówki nie mają tam czego robić.

Jak działają timeouty i dlaczego nie leczą 504?

nginx nie mierzy jednego czasu dla całego żądania. Rozdziela trzy etapy:

  • proxy_connect_timeout ogranicza zestawienie połączenia z upstreamem;
  • proxy_send_timeout liczy czas między kolejnymi zapisami do upstreamu, nie całe wysyłanie;
  • proxy_read_timeout liczy czas między kolejnymi odczytami z upstreamu, nie cały transfer.

Dokumentacja nginx podaje dla wszystkich trzech domyślne 60 sekund, a dla connect dodatkowo zależny od systemu limit 75 sekund. Te same liczby znaczą jednak co innego na każdym etapie - to opis zachowania nginx, nie rekomendacja dla twojej aplikacji.

Jeśli ustawiasz wartości jawnie, wyprowadź je z tego, ile twoja aplikacja naprawdę potrzebuje na odpowiedź:

proxy_connect_timeout CZAS_POLACZENIA;
proxy_send_timeout CZAS_WYSYLANIA;
proxy_read_timeout CZAS_CISZY_MIEDZY_ODCZYTAMI;

Nie wpisuj tu dużej liczby jako naprawy błędu 504. Ten kod oznacza, że proxy nie doczekało się odpowiedzi upstreamu w wymaganym czasie - większy limit przesuwa tylko moment zgłoszenia problemu, a powolna aplikacja zostaje powolna. Diagnozę istniejącego 504 prowadzi osobny poradnik o przekroczeniu czasu odpowiedzi.

Czy nginx sam ponowi żądanie?

Domyślne proxy_next_upstream obejmuje error i timeout, ale przejście do następnego upstreamu jest możliwe tylko zanim nginx wyśle cokolwiek klientowi. Metody nieidempotentne, takie jak POST, nie są ponawiane po wysłaniu żądania do upstreamu - chyba że jawnie dodasz parametr non_idempotent. Nie rób tego jako uniwersalnej naprawy: aplikacja może wtedy wykonać tę samą operację dwa razy.

Przy jednym wpisie server w grupie upstream retry nie wyczaruje zapasowej instancji. Kilka procesów i rozkładanie ruchu między nie to decyzja o architekturze, nie dopisek do tej konfiguracji.

Sprawdź składnię, przeładuj i przetestuj dwie ścieżki

Polecenia wykonuj na hoście nginx, z uprawnieniami właściwymi dla tej instalacji:

  1. Sprawdź składnię i dostępność plików wskazanych w konfiguracji: nginx -t.
  2. Po pozytywnym wyniku przeładuj konfigurację: nginx -s reload. Master nginx sprawdza nową konfigurację i przy błędzie zostaje przy starej - reload nie zrzuci działającego serwisu.
  3. Ponów test bezpośredni z przestrzeni nginx: curl -i http://ADRES_UPSTREAMU:PORT_APLIKACJI/SCIEZKA_TESTOWA.
  4. Z miejsca mającego dostęp do listenera wykonaj to samo żądanie przez proxy: curl -i SCHEMAT_PUBLICZNY://DOMENA:PORT_WEJSCIOWY/SCIEZKA_TESTOWA.

W obu testach zachowaj tę samą ścieżkę i oczekiwaną klasę odpowiedzi. Pierwszy sprawdza aplikację bez proxy, drugi listener i przekazanie żądania.

Gdy test bezpośredni przechodzi, a test przez proxy nie, porównaj konfigurację z wpisem w logu dla tego jednego żądania - problem leży w warstwie proxy. Gdy oba testy nie przechodzą, zostaw konfigurację w spokoju, bo najpierw aplikacja musi odpowiedzieć na badanej ścieżce.

Objaw, warstwa i następny krok

Warstwa Co musi być spełnione Jak to sprawdzisz Kod przy błędzie Gdzie szukać dalej
Proces Node Odpowiada na badanej ścieżce curl -i z przestrzeni nginx 502 albo 504 na proxy Warstwa aplikacji
Adres i port nginx wskazuje osiągalny listener Wpis server kontra udany test bezpośredni 502 Diagnoza błędu 502
Protokół Schemat zgodny z aplikacją proxy_pass http:// albo https:// 502 Diagnoza błędu 502
Ścieżka Aplikacja dostaje właściwy adres Porównanie żądania bez proxy i przez proxy Zależny od aplikacji Reguły location i URI w proxy_pass
Host Aplikacja dostaje wymaganą nazwę proxy_set_header Host $host Zależny od aplikacji Konfiguracja aplikacji
TLS do upstreamu SNI i walidacja działają Cztery dyrektywy proxy_ssl_* 502 Diagnoza błędu 502
WebSocket Ścieżka przekazuje Upgrade i Connection Odpowiedź 101 na żądanie zmiany protokołu Brak kodu 101 Lokalizacja WebSocket
Timeout Limity zgodne z czasem pracy aplikacji Trzy dyrektywy timeout 504 Diagnoza błędu 504
Zmiana konfiguracji Składnia poprawna, oba testy przechodzą nginx -t, reload, dwa testy curl 502 albo 504 Log proxy dla badanego żądania

Czego ta konfiguracja nie naprawi?

Poprawnie ustawione proxy usuwa rozjazdy między warstwami: zły adres, zły port, zły schemat, zgubioną ścieżkę, brakujące nagłówki. Nie wskrzesi jednak zatrzymanego procesu Node, nie dołoży brakującej pamięci i nie przyspieszy operacji, która trwa dłużej, niż ktokolwiek założył. Kod 502 dalej będzie oznaczał nieprawidłową odpowiedź upstreamu, a 504 brak odpowiedzi w wymaganym czasie - konfiguracja zmniejsza liczbę okazji do obu, nie gwarantuje ich braku.

Ustal też, kto w zespole odpowiada za warstwę proxy, a kto za aplikację. Ta granica rozstrzyga podczas awarii, kto wstaje do problemu - konfiguracja jej nie zastąpi. Z jakich warstw składa się cały serwis i która za co odpowiada, pokazuje przegląd warstw serwera strony.

Pytania o reverse proxy

Co robi reverse proxy?

Reverse proxy przyjmuje żądanie klienta, przekazuje je do skonfigurowanego upstreamu i zwraca jego odpowiedź. W nginx odpowiada za to moduł proxy z dyrektywą proxy_pass. Każda dodatkowa funkcja - walidacja certyfikatu upstreamu, WebSocket, nagłówki dla aplikacji - wymaga jawnej konfiguracji; sama obecność proxy niczego z tej listy nie włącza.

Czy nginx nadaje się na reverse proxy dla Node?

Tak, i dokumentacja nginx opisuje ten układ wprost. proxy_pass kieruje żądania do procesu Node przez HTTP albo HTTPS, a adres, port i protokół muszą zgadzać się z testem bezpośrednim z przestrzeni nginx. Przed przeładowaniem uruchom nginx -t, po nim porównaj ten sam endpoint bez proxy i przez publiczny listener.

Czym różni się reverse proxy od zwykłego proxy?

Kierunkiem pracy. Zwykłe proxy, nazywane forward proxy, działa po stronie klienta i ukrywa go przed serwerami. Reverse proxy działa po stronie serwera: przyjmuje ruch z internetu w imieniu aplikacji, która sama nie jest wystawiona na świat. Ta strona opisuje wyłącznie drugi układ - nginx przed aplikacją Node.

Skąd błąd 502 przy reverse proxy?

Kod 502 zgłasza proxy, gdy dostanie od upstreamu odpowiedź, której nie umie przyjąć - albo nie dostanie jej wcale. W konfiguracji sprawdź adres, port, protokół, ścieżkę i połączenie TLS do aplikacji, a test zacznij od odtworzenia żądania bezpośrednio z przestrzeni nginx. Pełną diagnozę istniejącego błędu prowadzi poradnik o 502 Bad Gateway.

Jak ustawić timeout, żeby nie dostać 504?

Rozdziel trzy limity - proxy_connect_timeout dla zestawienia połączenia, proxy_send_timeout dla przerw między zapisami i proxy_read_timeout dla przerw między odczytami. Wartości wyprowadź z tego, ile twoja aplikacja naprawdę potrzebuje na odpowiedź. Podbijanie jednej liczby na ślepo przesuwa moment zgłoszenia błędu, ale nie usuwa powolności aplikacji.

Czy reverse proxy to VPN?

Nie. VPN tworzy szyfrowany tunel dla całego ruchu urządzenia lub sieci, a reverse proxy obsługuje pojedyncze żądania HTTP do konkretnych aplikacji za nim. Opisana tu konfiguracja kończy się na nginx przekazującym żądania do jednej aplikacji Node - wniosków o VPN z niej nie wyciągniesz.

Gdzie sięgnąć po szczegóły?

Zachowanie wszystkich dyrektyw proxy opisuje dokumentacja nginx w modułach ngx_http_proxy_module i ngx_http_upstream_module oraz w poradniku o przekazywaniu WebSocket. Definicje kodów 101, 502 i 504 znajdziesz w RFC 9110. Gdy błąd 502 już wystąpił, jego diagnozę krok po kroku prowadzi poradnik 502 Bad Gateway, a przekroczenie czasu odpowiedzi rozkłada na etapy poradnik 504 Gateway Timeout.