Artykuł Serwery

Nginx jako reverse proxy - konfiguracja krok po kroku

Aby ustawić nginx jako reverse proxy, w /etc/nginx/sites-available/ tworzysz blok server z location / { proxy_pass http://127.0.0.1:PORT; }, dodajesz nagłówki proxy_set_header, aktywujesz plik symlinkiem do sites-enabled, sprawdzasz składnię przez nginx -t i przeładowujesz systemctl reload nginx. Poniżej pełna konfiguracja krok po kroku.

W skrócie: masz aplikację działającą lokalnie na jakimś porcie - Node na 3000, kontener w Dockerze, panel na 8080 - i chcesz wystawić ją na świat pod własną domeną z czystym URL, bez portu w adresie. Nginx staje przed aplikacją, odbiera ruch na porcie 80/443 i przekazuje go dalej. Rozkładam to na gotowe bloki konfiguracyjne, które wkleisz i uruchomisz.

Co robi nginx jako reverse proxy

Reverse proxy to serwer, który stoi przed Twoją aplikacją i pośredniczy w ruchu: klient łączy się z nginx, a nginx przekazuje żądanie do serwera aplikacyjnego (upstream) i zwraca odpowiedź klientowi. Przeglądarka nie łączy się bezpośrednio z aplikacją - widzi tylko nginx.

Po co Ci to na jednym serwerze:

  • jeden punkt wejścia na porcie 80 i 443, zamiast wystawiania aplikacji na dziwnym porcie,
  • terminacja SSL w jednym miejscu (certyfikat trzymasz na nginx, nie w każdej aplikacji z osobna),
  • ukrycie realnych portów aplikacji przed światem,
  • wiele aplikacji i domen na jednym adresie IP,
  • miejsce na cache, kompresję i limity, zanim ruch dotrze do aplikacji.

Jeśli nie znasz jeszcze samego pojęcia, zacznij od wpisu reverse proxy - co to jest i jak działa. Tutaj skupiam się na praktyce: gotowej konfiguracji nginx.

Wymagania wstępne

Krok po kroku zakładam, że masz kilka rzeczy gotowych. Sprawdź:

  • serwer z Linuksem (Ubuntu albo Debian) i dostęp do konta root lub sudo,
  • zainstalowany nginx - po apt install nginx zweryfikuj wersję komendą nginx -v,
  • aplikację działającą lokalnie na znanym porcie, potwierdzoną poleceniem curl http://127.0.0.1:3000 (podstaw swój port),
  • domenę z rekordem A wskazującym na IP serwera - potrzebną, gdy dojdziesz do SSL.

Konfigurację robisz zdalnie, więc łączysz się z serwerem przez SSH, sites-enabled zawiera tylko symlinki do konfiguracji aktualnie aktywnych, a główny nginx.conf wciąga te symlinki dyrektywą include. Dzięki temu włączasz i wyłączasz stronę bez kasowania jej pliku.

Wklej minimalny, działający blok - to jest rdzeń całej konfiguracji reverse proxy:

server {
    listen 80;
    server_name moja-domena.pl;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

Konkret - co robi każda linia i co się psuje, gdy jej zabraknie:

  • listen 80; - nginx nasłuchuje na porcie HTTP. Bez tego blok nie odbierze ruchu.
  • server_name moja-domena.pl; - dla której domeny działa ten blok. Nginx po tej wartości wybiera, który server obsłuży żądanie.
  • proxy_pass http://127.0.0.1:3000; - adres i port aplikacji, do której trafia ruch. To serce reverse proxy. Podstaw realny port swojej aplikacji.
  • proxy_set_header Host $host; - przekazuje oryginalną nazwę domeny do aplikacji. Bez tego nginx wysyła własny nagłówek Host, przez co aplikacja generuje złe linki i przekierowania.
  • proxy_set_header X-Real-IP $remote_addr; - podaje aplikacji prawdziwy adres IP klienta. Inaczej w logach zobaczysz tylko adres localhost.
  • proxy_set_header X-Forwarded-For ...; - dokleja IP klienta do łańcucha proxy, ważne przy statystykach i limitach.
  • proxy_set_header X-Forwarded-Proto $scheme; - mówi aplikacji, czy klient przyszedł po http, czy https. Bez tego aplikacja wpada w pętlę przekierowań, gdy sama wymusza HTTPS.

Pro tip admina: nagłówki proxy_set_header to najczęściej pomijany element - config bez nich zwykle "działa", dopóki aplikacja nie zacznie generować dziwnych linków albo logować wszystkich jako 127.0.0.1. Ustaw je od razu.

Krok 2 - aktywuj konfigurację i przetestuj składnię

Plik w sites-available sam nic nie robi - musisz go włączyć symlinkiem do sites-enabled:

sudo ln -s /etc/nginx/sites-available/moja-aplikacja.conf /etc/nginx/sites-enabled/

Pułapka: domyślny plik default. Świeży nginx ma aktywną stronę powitalną, która przechwytuje ruch i potrafi przykryć Twój nowy blok. Wyłącz ją:

sudo rm /etc/nginx/sites-enabled/default

Teraz sprawdź składnię, zanim cokolwiek przeładujesz:

sudo nginx -t

To jest krok obowiązkowy, nie kosmetyka. reload z błędem w konfiguracji nie wywali działającego serwera (nginx odrzuci złą konfigurację i zostanie przy starej), ale pełny restart już tak - serwis padnie i strona zniknie. Dlatego zawsze nginx -t, a dopiero potem przeładowanie bez przerwy w działaniu:

sudo systemctl reload nginx

Sprawdź efekt z zewnątrz:

curl -I http://moja-domena.pl

Jeśli w odpowiedzi widzisz nagłówki swojej aplikacji, reverse proxy działa. Reverse proxy w tej formie odpowiada już na czysty adres domeny, bez portu - i to jest cały minimalny setup.

Krok 3 - dodaj HTTPS przez Let's Encrypt

Konfiguracja reverse proxy prawie zawsze kończy się na tym samym pytaniu: jak wpiąć HTTPS. Najprościej przez darmowe certyfikaty Let's Encrypt i narzędzie certbot, które samo dopisze konfigurację SSL do Twojego bloku.

Krok po kroku:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d moja-domena.pl

Certbot pobierze certyfikat, doda do pliku blok listen 443 ssl z dyrektywami ssl_certificate i ssl_certificate_key, a ruch z portu 80 przekieruje na 443. Po jego pracy blok wygląda mniej więcej tak:

server {
    listen 443 ssl;
    server_name moja-domena.pl;

    ssl_certificate /etc/letsencrypt/live/moja-domena.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/moja-domena.pl/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

Certyfikaty Let's Encrypt są ważne 90 dni, dlatego certbot instaluje automatyczne odnawianie. Sprawdź, czy licznik działa:

sudo systemctl status certbot.timer

Alternatywnie SSL i ochronę WAF możesz wystawić na brzegu sieci - przed nginx staje wtedy Cloudflare, terminuje HTTPS i filtruje ruch, zanim dotrze do Twojego serwera. Do prostego reverse proxy na jednym VPS certbot w zupełności wystarcza; Cloudflare bierzesz, gdy chcesz dodatkowej warstwy ochrony i cache na krawędzi.

Krok 4 - konfiguracje rozszerzone

Podstawa działa, ale realny ruch szybko wymusza kilka wariantów. Oto te, po które sięgasz najczęściej.

WebSocket - upgrade połączenia

Aplikacje z live-czatem, powiadomieniami czy panelem na żywo używają WebSocketów. Domyślny reverse proxy je zrywa, bo nie przekazuje nagłówków podniesienia protokołu. Dodaj do bloku location:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

Bez proxy_http_version 1.1 oraz pary nagłówków Upgrade i Connection połączenie WebSocket rozłącza się co kilkanaście sekund i klient wpada w pętlę ponownego łączenia.

Wiele domen i aplikacji na jednym serwerze

Trade-off jest tu prosty: zamiast wpychać wszystko do jednego pliku, trzymaj osobny .conf na każdą domenę. Każdy ma własny server_name i własny proxy_pass na inny port lokalny:

# /etc/nginx/sites-available/app-a.conf
server {
    listen 80;
    server_name app-a.pl;
    location / { proxy_pass http://127.0.0.1:3000; }
}
# /etc/nginx/sites-available/app-b.conf
server {
    listen 80;
    server_name app-b.pl;
    location / { proxy_pass http://127.0.0.1:4000; }
}

Nginx po nagłówku Host kieruje ruch do właściwego bloku. Każdy plik aktywujesz osobnym symlinkiem - dzięki temu wyłączenie jednej domeny nie rusza pozostałych.

localhost czy 127.0.0.1 w proxy_pass

To subtelna pułapka. proxy_pass http://localhost:3000 każe nginx rozwiązać nazwę localhost, a ta w nowoczesnych systemach wskazuje zarówno na 127.0.0.1 (IPv4), jak i na ::1 (IPv6). Jeśli aplikacja nasłuchuje tylko na IPv4, a nginx trafi na ::1, dostaniesz błąd połączenia. Konkret: w proxy_pass wpisuj jawnie http://127.0.0.1:3000, chyba że świadomie obsługujesz IPv6 po stronie aplikacji.

Docker - proxy_pass na nazwę kontenera

Gdy nginx i aplikacja stoją w tej samej sieci Dockera, nie kierujesz ruchu na 127.0.0.1, bo dla kontenera nginx to on sam, nie host. Zamiast tego w proxy_pass podajesz nazwę usługi albo kontenera, którą wewnętrzny DNS Dockera rozwiąże na właściwy adres - na przykład proxy_pass http://app:3000;, gdzie app to nazwa usługi z pliku Compose. Sam plik konfiguracyjny reverse proxy wygląda identycznie jak wyżej.

HTTP/2 dla szybszego ładowania

Gdy masz już SSL, włączenie HTTP/2 to jedna dyrektywa przy nasłuchiwaniu:

listen 443 ssl;
http2 on;

HTTP/2 lepiej multipleksuje wiele równoległych żądań na jednym połączeniu - odczujesz to zwłaszcza przy stronach z dużą liczbą zasobów.

Częste błędy i jak je naprawić

Reverse proxy psuje się w kilku powtarzalnych miejscach. Sprawdź po kolei:

  • 502 Bad Gateway - najczęstszy błąd. Aplikacja nie działa albo w proxy_pass masz zły port. Zweryfikuj curl http://127.0.0.1:PORT bezpośrednio na serwerze i systemctl status twoja-aplikacja, a szczegóły znajdziesz w /var/log/nginx/error.log. Na systemach z SELinuksem sprawdź też, czy nie blokuje on połączeń wychodzących nginx.
  • 504 Gateway Timeout - aplikacja odpowiada, ale zbyt wolno i nginx zrywa czekanie. Podnieś limity: proxy_read_timeout i proxy_connect_timeout, na przykład do 120 sekund. Jeśli to jednorazowa ciężka operacja - w porządku; jeśli stały objaw, problem jest w aplikacji, nie w nginx.
  • 403 Forbidden - zwykle brak dopasowanego bloku location, złe uprawnienia do plików albo przechwycenie ruchu przez pozostawiony default. Upewnij się, że wyłączyłeś domyślną stronę i że blok server_name faktycznie pasuje do domeny.
  • Zmiany nie działają - zwykle zapomniany symlink w sites-enabled (edytowałeś plik w sites-available, ale go nie włączyłeś) albo reload bez wcześniejszego nginx -t. Sprawdź, czy symlink istnieje i czy składnia jest poprawna.
  • Pętla przekierowań - aplikacja wymusza HTTPS, a nie dostaje informacji, że klient przyszedł już po https. Rozwiązanie: nagłówek proxy_set_header X-Forwarded-Proto $scheme; i poprawna obsługa tego nagłówka po stronie aplikacji.

Pro tip admina: przy każdym błędzie zaczynaj od dwóch źródeł - sudo nginx -t (składnia) i tail -f /var/log/nginx/error.log (co realnie się dzieje w czasie żądania). To rozwiązuje większość problemów szybciej niż zgadywanie po configu.

Kiedy reverse proxy, a kiedy wystarczy hosting

Reverse proxy ma sens tylko tam, gdzie masz pełny dostęp do serwera i portów - czyli na VPS albo dedyku z rootem. Na hostingu współdzielonym nie postawisz własnego bloku server nginx ani nie otworzysz portu aplikacji. To zależy od tego, co uruchamiasz: statyczną stronę czy WordPressa obsłuży dobry hosting współdzielony, ale własna aplikacja w Node, Pythonie czy kontenerze potrzebuje serwera, na którym rządzisz Ty.

Jeśli stoisz przed wyborem serwera pod taki scenariusz, sprawdź ranking hostingów VPS pod DevOps - porównuję tam opcje pod kątem dostępu SSH, crona, rsync i właśnie stawiania reverse proxy. Cały serwer administrujesz zdalnie przez SSH.

FAQ

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

Zwykłe proxy (forward proxy) działa po stronie klienta - pośredniczy w ruchu wychodzącym użytkownika do internetu i ukrywa jego adres. Reverse proxy działa po stronie serwera - stoi przed aplikacją, przyjmuje ruch przychodzący od klientów i rozdziela go do serwerów aplikacyjnych. Pełne rozróżnienie rozkładam we wpisie proxy vs reverse proxy.

Jak skonfigurować nginx reverse proxy na localhost?

W bloku location ustaw proxy_pass http://127.0.0.1:PORT, podstawiając port, na którym nasłuchuje aplikacja. Wpisuj jawnie 127.0.0.1, a nie localhost - nazwa localhost rozwiązuje się też na adres IPv6 ::1, więc gdy aplikacja słucha tylko na IPv4, przez localhost dostaniesz błąd połączenia. Jawny adres IPv4 usuwa tę pułapkę.

Jak dodać HTTPS do reverse proxy nginx?

Zainstaluj certbota poleceniem sudo apt install certbot python3-certbot-nginx i uruchom sudo certbot --nginx -d twoja-domena.pl. Certbot pobierze darmowy certyfikat Let's Encrypt, sam dopisze do konfiguracji blok listen 443 ssl oraz przekierowanie z portu 80 na 443 i ustawi automatyczne odnawianie. Certyfikaty są ważne 90 dni i odnawiają się bez Twojego udziału.

Dlaczego nginx zwraca 502 Bad Gateway przy reverse proxy?

Najczęstsza przyczyna to niedziałająca aplikacja docelowa albo zły port w proxy_pass. Sprawdź bezpośrednio na serwerze poleceniem curl http://127.0.0.1:PORT, czy aplikacja odpowiada, oraz systemctl status, czy proces żyje. Zajrzyj do /var/log/nginx/error.log po szczegóły. Na systemach z SELinuksem sprawdź też, czy nie blokuje on połączeń wychodzących nginx.

Czy mogę mieć wiele aplikacji za jednym nginx?

Tak i to jeden z głównych powodów, by go używać. Utwórz osobny plik .conf dla każdej domeny w sites-available, każdy z własnym server_name i proxy_pass na inny port lokalny, a następnie aktywuj je symlinkami do sites-enabled. Nginx po nagłówku Host kieruje każde żądanie do właściwego bloku server. Jeden serwer i jeden adres IP obsłużą wtedy wiele aplikacji naraz.