Czym jest reverse proxy i dlaczego warto?

Reverse proxy to serwer, który stoi między klientem (przeglądarką) a serwerem aplikacji. Zamiast łączyć się bezpośrednio z Twoją aplikacją, użytkownik łączy się z Nginx, który przekazuje żądanie do właściwego serwisu i zwraca odpowiedź.

Brzmi jak niepotrzebna warstwa pośrednia? W praktyce reverse proxy rozwiązuje wiele problemów naraz:

  • SSL termination — Nginx obsługuje szyfrowanie HTTPS, a aplikacja nie musi się tym zajmować
  • Serwowanie plików statycznych — Nginx jest znacznie szybszy w serwowaniu obrazów, CSS i JS niż serwer aplikacji
  • Load balancing — rozdzielanie ruchu między wiele instancji aplikacji
  • Cache — przechowywanie odpowiedzi w pamięci, odciążając serwer aplikacji
  • Rate limiting — ochrona przed nadmiernym ruchem i prostymi atakami DDoS
  • Jeden punkt wejścia — wiele aplikacji na różnych portach dostępnych przez jedną domenę

Ten ostatni punkt jest szczególnie istotny, gdy na jednym serwerze hostujesz kilka serwisów webowych. Każdy działa na swoim porcie wewnętrznym, a Nginx kieruje ruch na podstawie nazwy domeny.

Podstawowa konfiguracja

Załóżmy, że masz aplikację webową działającą na porcie 8080 i chcesz, żeby była dostępna pod domeną example.com. Oto minimalna konfiguracja Nginx jako reverse proxy:

server {
    listen 80;
    server_name example.com www.example.com;

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

Omówmy kluczowe elementy:

proxy_pass — najważniejsza dyrektywa. Wskazuje, dokąd Nginx ma przekazywać żądania. Może to być localhost:port, adres IP w sieci wewnętrznej lub nazwa kontenera Docker.

proxy_set_header Host — przekazuje oryginalną nazwę domeny do aplikacji. Bez tego aplikacja zobaczyłaby localhost:8080 zamiast example.com.

proxy_set_header X-Real-IP — przekazuje prawdziwy adres IP klienta. Bez tego aplikacja widziałaby IP serwera Nginx (zwykle 127.0.0.1).

proxy_set_header X-Forwarded-For — łańcuch adresów IP, przez które przeszło żądanie. Ważne, gdy masz wiele warstw proxy.

proxy_set_header X-Forwarded-Proto — informacja, czy oryginalne żądanie było HTTP czy HTTPS.

Wiele aplikacji na jednym serwerze

Prawdziwa moc reverse proxy ujawnia się, gdy hostujesz wiele serwisów. Każda domena trafia do innego kontenera lub procesu:

# Strona firmowa
server {
    listen 80;
    server_name www.firma.pl;

    location / {
        proxy_pass http://strona-firmowa:8080;
        include /etc/nginx/proxy-params.inc;
    }
}

# Aplikacja do faktur
server {
    listen 80;
    server_name faktury.firma.pl;

    location / {
        proxy_pass http://app-faktury:3000;
        include /etc/nginx/proxy-params.inc;
    }
}

# Blog
server {
    listen 80;
    server_name blog.firma.pl;

    location / {
        proxy_pass http://blog:8082;
        include /etc/nginx/proxy-params.inc;
    }
}

Wszystkie trzy serwisy są dostępne na porcie 80 (standardowy HTTP), a Nginx kieruje ruch na podstawie nagłówka Host. Powtarzające się dyrektywy proxy_set_header wydzielamy do wspólnego pliku include, żeby uniknąć duplikacji.

Dodanie HTTPS

W praktyce każda produkcyjna konfiguracja powinna używać HTTPS. Z Certbotem i Let's Encrypt to kwestia jednego polecenia, ale warto rozumieć wynikową konfigurację:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/ssl/certs/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/certs/example.com/privkey.pem;

    # Silna konfiguracja TLS
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=63072000" always;

    location / {
        proxy_pass http://localhost:8080;
        include /etc/nginx/proxy-params.inc;
    }
}

Pierwszy blok server przekierowuje wszystkie żądania HTTP na HTTPS (kod 301). Drugi blok obsługuje ruch HTTPS z certyfikatem SSL. Aplikacja za proxy dalej działa na zwykłym HTTP — szyfrowanie odbywa się tylko między klientem a Nginx (tzw. SSL termination).

Load balancing

Gdy jedna instancja aplikacji nie wystarczy, Nginx może rozdzielać ruch między wiele backendów:

upstream app_servers {
    server app1:8080 weight=3;
    server app2:8080 weight=2;
    server app3:8080 weight=1;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    location / {
        proxy_pass http://app_servers;
        include /etc/nginx/proxy-params.inc;
    }
}

Dyrektywa upstream definiuje grupę serwerów. Parametr weight kontroluje proporcje rozdzielania ruchu — w tym przykładzie app1 otrzyma 3 razy więcej żądań niż app3. Nginx wspiera też inne algorytmy: least_conn (kieruje do serwera z najmniejszą liczbą aktywnych połączeń) i ip_hash (ten sam klient zawsze trafia do tego samego serwera).

Serwowanie plików statycznych

Dobrą praktyką jest serwowanie plików statycznych bezpośrednio przez Nginx, bez angażowania serwera aplikacji:

location /static/ {
    alias /var/www/static/;
    expires 30d;
    add_header Cache-Control "public, immutable";
}

location / {
    proxy_pass http://localhost:8080;
    include /etc/nginx/proxy-params.inc;
}

Żądania do /static/ są obsługiwane bezpośrednio z dysku, z nagłówkiem cache na 30 dni. Reszta ruchu trafia do aplikacji. To znacząco odciąża serwer aplikacji i przyspiesza ładowanie strony.

Rate limiting — ochrona przed nadużyciami

Nginx pozwala ograniczyć liczbę żądań z jednego adresu IP:

limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

server {
    location / {
        limit_req zone=general burst=20 nodelay;
        proxy_pass http://localhost:8080;
    }
}

Ta konfiguracja pozwala na 10 żądań na sekundę z jednego IP, z buforem 20 dodatkowych żądań. Przekroczenie limitu skutkuje odpowiedzią 503. To prosta, ale skuteczna ochrona przed prostymi atakami i botami.

Podsumowanie

Nginx jako reverse proxy to standard w nowoczesnej infrastrukturze webowej. Zapewnia SSL termination, load balancing, cache, rate limiting i możliwość hostowania wielu serwisów na jednym serwerze. Konfiguracja jest deklaratywna i czytelna, a społeczność dostarcza rozwiązania na praktycznie każdy scenariusz. Jeśli Twoja aplikacja webowa jest wystawiona bezpośrednio do internetu bez reverse proxy — warto to zmienić.