Skip to content Skip to footer

Co to jest serwer Nginx? W jaki sposób wykonać poprawną …

Nginx to popularny serwer WWW i reverse proxy, który często działa „na froncie” infrastruktury, obsługując ruch HTTP/HTTPS zanim trafi on do aplikacji. Gdy zależy Ci na stabilnym serwowaniu statycznych plików, terminowaniu TLS oraz przekazywaniu żądań do backendu (np. na port 3000/8080), Nginx bywa jednym z najczęstszych wyborów. Jego model oparty o zdarzenia (event-driven) zazwyczaj lepiej znosi dużą liczbę równoległych połączeń niż podejście „wątek na połączenie”. W praktyce dobra konfiguracja sprowadza się do zrozumienia bloków server i location, logów oraz zasad bezpiecznego wdrażania zmian. W tym poradniku przechodzimy od tego, czym jest Nginx i do czego służy, przez instalację na Ubuntu/Debian krok po kroku. Czytaj dalej, jeśli zależy Ci na przewidywalnym wdrożeniu bez typowych pułapek typu 502/404 po pierwszym uruchomieniu.

czym jest nginx i do czego służy

Nginx (czyt. „engine-x”) to serwer HTTP oraz reverse proxy, który obsługuje ruch sieciowy w modelu zdarzeniowym (event-driven), zamiast podejścia „wątek na połączenie”. Dzięki temu zwykle lepiej radzi sobie z dużą liczbą równoległych połączeń, w tym keep-alive, niż klasyczny model procesowy. Najczęściej wykorzystuje się go do serwowania statycznych plików (HTML/CSS/JS), terminowania TLS (HTTPS), pracy jako reverse proxy dla aplikacji (np. Node.js, Python, Java) oraz do load balancingu. W typowej architekturze Nginx działa na porcie 443 i przekazuje ruch do backendów uruchomionych lokalnie lub w sieci (np. 127.0.0.1:3000 albo 8080).

Nginx w wersji open source jest darmowy i zazwyczaj wystarcza w większości zastosowań produkcyjnych, natomiast Nginx Plus rozszerza go m.in. o zaawansowane metryki, aktywne health-checki oraz sprawniejsze zarządzanie upstreamami. Konfiguracja opiera się na kontekstach (np. http, server, location), a domenę i port ustawisz w bloku server za pomocą listen oraz server_name. Wiele stron (wirtualne hosty) realizuje się przez kilka bloków server, a na Debian/Ubuntu często przechowuje się je w /etc/nginx/sites-available i aktywuje linkami w sites-enabled. W razie problemów pierwszym miejscem diagnostyki są logi w /var/log/nginx/access.log oraz /var/log/nginx/error.log, zwłaszcza gdy pojawiają się błędy 502/404.

Nginx nie uruchamia kodu aplikacji (np. Python/Node), więc do obsługi „logiki” potrzebujesz osobnego backendu, takiego jak Gunicorn, uWSGI, Node.js czy Tomcat. Jego rola to warstwa HTTP, która routuje, buforuje i zabezpiecza ruch przed aplikacją. W porównaniu do Apache, Nginx zwykle ma mniejszy narzut pamięci i lepszą wydajność przy dużej liczbie połączeń keep-alive, podczas gdy Apache bywa prostszy w scenariuszach opartych o .htaccess oraz dynamiczne moduły. Jeśli celem jest nowoczesne reverse proxy i stabilna obsługa wielu połączeń, Nginx często okazuje się praktycznym wyborem „na front”.

instalacja nginx na ubuntu i debian

Nginx na Ubuntu/Debian zainstalujesz poleceniem „sudo apt update && sudo apt install nginx”. Po zakończeniu instalacji warto upewnić się, że usługa działa, używając „systemctl status nginx”, a potem wejść w przeglądarce na adres IP serwera i sprawdzić, czy wyświetla się strona domyślna. Jeżeli Nginx ma uruchamiać się po restarcie serwera, włącz autostart przez „systemctl enable –now nginx”. To w praktyce wyjaśnia problem „czemu po reboot Nginx nie wstaje”, ponieważ bez enable usługa nie startuje automatycznie.

Główny plik konfiguracji znajduje się zwykle pod /etc/nginx/nginx.conf, a konfiguracje serwisów są dołączane dyrektywą include (np. conf.d/*.conf), przy czym na Ubuntu często spotyka się układ sites-available/sites-enabled. Zanim przeładujesz konfigurację na produkcji, uruchom „sudo nginx -t”, aby wychwycić błędy składni i brakujące pliki (np. certyfikaty). Przy wdrażaniu zmian lepiej wybierać „systemctl reload nginx”, bo przeładowuje konfigurację bez zrywania aktywnych połączeń, podczas gdy „restart” potrafi przerwać ruch. Po wdrożeniu szybko zweryfikujesz odpowiedzi nagłówkami przez „curl -I http://twoja-domena.pl” oraz „curl -I https://twoja-domena.pl”, a przy przekierowaniach 301/302 sprawdzisz reguły w server/location i to, czy TLS jest podpięty prawidłowo.

podstawy konfiguracji: server, location, root

Konfiguracja Nginx opiera się na blokach server (wirtualny host) i location (reguły dopasowania ścieżek), natomiast katalog z plikami strony wskazuje dyrektywa root. Minimalny virtual host może wyglądać tak: „server { listen 80; server_name example.com; root /var/www/example; }”, co od razu porządkuje kwestię „gdzie ustawić domenę i katalog”. Dyrektywa „index index.html;” określa, jaki plik ma być serwowany domyślnie po wejściu do katalogu. Przy kilku serwisach mechanika routingu pozostaje taka sama, bo o wyborze decyduje dopasowanie w server i location.

Bloki location mogą dopasowywać prefiks (np. „location /api/”) albo wyrażenie regularne (np. „location ~* \.png$”), a w razie konfliktów kluczowe są priorytety dopasowań (m.in. exact „=”, potem prefiksy, a regexy rządzą się własnymi zasadami wyboru). Gdy pojawiają się nieoczekiwane 404, często pomaga kontrola zachowania przez „try_files”, np. „try_files $uri $uri/ =404;”. Dla aplikacji SPA (React/Vue) standardem bywa „try_files $uri /index.html;”, żeby odświeżenie podstrony nie kończyło się 404. W praktyce to najczęstsza różnica między „działa na stronie głównej” a „sypie się na routingu po stronie klienta”.

Różnica między „root” a „alias” jest kluczowa, ponieważ „root” dokleja URI do katalogu, natomiast „alias” podstawia ścieżkę bezpośrednio dla wskazanej lokalizacji (np. „location /static/ { alias /srv/app/static/; }”). Błąd 403 najczęściej sygnalizuje problem z uprawnieniami do katalogu albo brak pliku index przy wyłączonym autoindex, a dostęp potrafią dodatkowo ograniczać SELinux/AppArmor. Przekierowania ustawisz przez „return 301 …” (stałe) lub „return 302 …” (tymczasowe), a przy migracji HTTP→HTTPS zwykle warto dopilnować 301. Dla spójnego UX możesz też zdefiniować własne strony błędów, np. „error_page 404 /404.html;”, żeby użytkownik nie oglądał domyślnej strony Nginx.

reverse proxy do aplikacji node, python, java

Reverse proxy w Nginx najczęściej konfigurujesz przez „proxy_pass” w bloku location, przekierowując ruch do aplikacji działającej na porcie backendu. Dla Node.js typowy wzorzec to „location / { proxy_pass http://127.0.0.1:3000; }”, a gdy pojawia się 502, w pierwszej kolejności sprawdzasz port oraz to, czy proces faktycznie nasłuchuje na właściwym adresie (127.0.0.1 vs 0.0.0.0). Aby aplikacja widziała poprawny host i IP klienta, dodaj „proxy_set_header Host $host;” oraz „proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;”. To często rozwiązuje kłopoty z logowaniem, generowaniem linków i identyfikacją użytkownika po stronie backendu.

Długie żądania (np. eksport danych) wymagają dopasowania timeoutów, bo błędy po około 60 sekundach zwykle wynikają z limitów po stronie Nginx lub warstwy pośredniej. W takiej sytuacji zwiększasz „proxy_read_timeout” i „proxy_connect_timeout”, a potem weryfikujesz zachowanie endpointu. Przy streamingu albo SSE/WS częstym źródłem opóźnień bywa buforowanie, więc ustawiasz „proxy_buffering off;”. Dzięki temu dane mogą trafiać do klienta na bieżąco, bez sztucznego „czekania” na większe porcje odpowiedzi.

Dla WebSocketów musisz jawnie włączyć tryb zgodny z upgrade, czyli „proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";”, inaczej połączenie może się rwać albo nie przejdzie w tryb WS. Jeśli chcesz rozłożyć ruch na kilka instancji, definiujesz pulę w „upstream app { server 10.0.0.10:3000; server 10.0.0.11:3000; }” i kierujesz na „proxy_pass http://app;”. Gdy aplikacja trzyma sesję w pamięci, przydaje się „ip_hash” (albo przeniesienie sesji do Redis), bo bez tego użytkownik może trafiać na różne instancje i „gubić” sesję. W open source Nginx failover opiera się na błędach i timeoutach (np. „max_fails=3 fail_timeout=30s”), natomiast aktywne health-checki wymagają Nginx Plus lub narzędzi zewnętrznych.

serwowanie statycznych plików i optymalizacja frontendu

Nginx bardzo dobrze sprawdza się przy serwowaniu statycznych plików (HTML/CSS/JS) i to właśnie w tym miejscu najprościej „na szybko” podnieść odczuwalną wydajność frontendu. Kompresję aktywujesz dyrektywami „gzip on;” oraz „gzip_types text/css application/javascript application/json;”, co często bywa jednym z pierwszych działań, gdy analizujesz niski wynik PageSpeed. Brotli potrafi działać szybciej, ale wymaga dodatkowego modułu (często jako pakiet nginx-module-brotli), więc dostępność zależy od dystrybucji i sposobu budowania. Jeśli zależy Ci na realnym skróceniu czasu ładowania CSS/JS bez ruszania aplikacji, zacznij od kompresji i poprawnie ustawionych nagłówków cache.

Cache dla plików wersjonowanych ustawisz przez „expires 30d; add_header Cache-Control "public, immutable";”, co zmniejsza liczbę requestów przy kolejnych wizytach. Nginx może też korzystać z ETag oraz nagłówków modyfikacji, aby zwracać 304 Not Modified, o ile zasoby statyczne są podawane bezpośrednio przez Nginx, a nie „przez aplikację”. Przy większych plikach (np. wideo) przydają się „sendfile on; tcp_nopush on; tcp_nodelay on;”, ponieważ ograniczają narzut kopiowania danych i poprawiają throughput. Gdy przeglądarka pobiera plik zamiast go wyświetlać (np. SVG), zweryfikuj mapowanie MIME types (plik mime.types) oraz to, czy ustawiany jest właściwy Content-Type.

Dostęp do wrażliwych plików zablokujesz regułą „location ~ /(\.env|\.git) { deny all; }”, co chroni przed typowymi skanami botów polujących na sekrety. Listowanie katalogów włączane przez „autoindex on;” ułatwia publikację plików, ale w środowisku produkcyjnym zwykle bywa ryzykowne — jeśli widzisz „spis plików”, wyłącz autoindex albo dodaj plik index. Jeśli upload kończy się błędem 413, podnieś limit „client_max_body_size” (np. do 50m), pamiętając, że jest to ograniczenie po stronie Nginx, niezależne od limitów w aplikacji i przeglądarce. Dla aplikacji SPA (React/Vue) typowy fragment to „location / { root /var/www/app; try_files $uri $uri/ /index.html; }”, co usuwa 404 przy bezpośrednim wejściu na ścieżki typu /dashboard.

https/tls: certyfikaty i twarde ustawienia

HTTPS w Nginx najszybciej uruchomisz przez Let’s Encrypt i narzędzie certbot, używając „certbot –nginx -d example.com -d www.example.com”. Certbot potrafi automatycznie dopisać konfigurację dla 443 oraz przekierowanie z 80, ale po każdej modyfikacji warto wykonać „nginx -t”, aby wyłapać błędy składni i brakujące pliki certyfikatów. Jeśli ustawiasz TLS ręcznie (np. przy innym kliencie ACME lub certyfikatach firmowych), użyjesz m.in. „listen 443 ssl http2;” oraz wskażesz „ssl_certificate …/fullchain.pem;” i „ssl_certificate_key …/privkey.pem;”. Poprawne wymuszenie HTTPS realizuje osobny server na porcie 80, który zwraca „return 301 https://$host$request_uri;”.

Twardsze ustawienia TLS zwykle zaczyna się od zawężenia protokołów do „ssl_protocols TLSv1.2 TLSv1.3;”. Zanim wyłączysz starsze wersje, dobrze jest upewnić się, że nie odetniesz klientów legacy. HSTS włączysz nagłówkiem „add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;”, ale rób to z rozwagą, bo po aktywacji przeglądarki będą wymuszać HTTPS, a błędny certyfikat może zablokować dostęp użytkownikom. OCSP stapling uruchomisz przez „ssl_stapling on; ssl_stapling_verify on;” wraz z poprawnie ustawionym resolverem DNS, co przyspiesza walidację certyfikatu. Do sprawdzenia łańcucha certyfikatów przydaje się „openssl s_client -connect example.com:443 -servername example.com”, a ocenę konfiguracji (np. słabe szyfry, brak HSTS) ułatwia zewnętrzny test Qualys SSL Labs.

W praktyce utrzymanie HTTPS to również pilnowanie odnowień, bo Let’s Encrypt wymaga ich co 90 dni, a certbot zwykle instaluje timer systemd lub cron. Żeby nie zaliczyć przestoju z powodu wygasłego certyfikatu, sprawdź harmonogram przez „systemctl list-timers | grep certbot” i co jakiś czas uruchamiaj „certbot renew –dry-run”. HTTP/2 włączysz przez „listen 443 ssl http2;”, co zazwyczaj pomaga przy stronach z wieloma zasobami, natomiast HTTP/3 (QUIC) zależy od wersji oraz sposobu zbudowania Nginx. Poniżej jest krótka lista komend, które najczęściej przydają się przy wdrożeniu i kontroli TLS:

  • „nginx -t” – sprawdzenie konfiguracji przed reloadem oraz po zmianach certyfikatów.
  • „openssl s_client -connect example.com:443 -servername example.com” – szybkie sprawdzenie łańcucha certyfikatów.
  • „systemctl list-timers | grep certbot” – potwierdzenie, że odnowienia są zaplanowane.
  • „certbot renew –dry-run” – test odnowienia bez ryzyka „niespodzianki” w dniu wygaśnięcia.

Wydajność: workerzy, cache, rate limiting

Wydajność Nginx najłatwiej poprawić przez sensowne ustawienia workerów, limitów połączeń i cache, tak aby serwer stabilnie obsługiwał duży, równoległy ruch. Dla większości maszyn dobrym punktem startowym jest „worker_processes auto;” oraz podniesienie „worker_connections” (np. 4096 lub 8192), bo bezpośrednio przekłada się to na liczbę jednoczesnych połączeń. Przybliżenie maksymalnej współbieżności to worker_processes * worker_connections (z uwzględnieniem narzutów), co pozwala szybko ocenić, czy całość ma szansę się spiąć przy danym obciążeniu. Jeśli masz problem z „duszeniem się” Nginx przy wielu połączeniach, zacznij od worker_processes i worker_connections, zanim przejdziesz do strojenia aplikacji.

Opóźnienia przy API często da się obniżyć, utrzymując połączenia keep-alive, ponieważ przy wielu krótkich requestach spada koszt zestawiania TCP. Konfiguracja „keepalive_timeout 65;” oraz keepalive po stronie upstreamów potrafi zauważalnie odciążyć backend i skrócić czasy odpowiedzi bez ruszania kodu. Gdy ruch jest nierówny, zwłaszcza w godzinach szczytu, buforowanie odpowiedzi backendu przez „proxy_cache_path” i „proxy_cache” pomaga zmniejszyć liczbę trafień do aplikacji. Jeśli jednak po zalogowaniu użytkownicy widzą nieaktualne dane, zwykle brakuje reguły omijania cache, na przykład „proxy_cache_bypass $http_authorization;”.

Nadużycia i „zalew” requestami ograniczysz przez rate limiting oraz limity równoległych połączeń na IP. Do limitowania zapytań użyj „limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;” oraz „limit_req zone=api burst=20 nodelay;”, a dla concurrency zastosuj „limit_conn_zone $binary_remote_addr zone=addr:10m;” i „limit_conn addr 20;”. Zbyt intensywne logowanie potrafi podbić iowait, więc gdy dysk staje się wąskim gardłem, warto rozważyć buforowanie lub logowanie selektywne, na przykład bez health-checków, oraz dopilnować rotacji. Do pomiarów, zamiast działać po omacku, wykorzystaj benchmarki „wrk -t4 -c200 -d30s https://example.com/” lub „ab -n 10000 -c 200”, a przy dużej liczbie statycznych plików sprawdź też „open_file_cache max=10000 inactive=30s;”.

  • „worker_processes auto;” + wyższe „worker_connections” (np. 4096/8192) – podstawa przy dużej liczbie połączeń.
  • „keepalive_timeout 65;” i keepalive upstream – niższe koszty TCP przy wielu krótkich żądaniach.
  • „proxy_cache_path”/„proxy_cache” + „proxy_cache_bypass $http_authorization;” – cache z kontrolą dla treści autoryzowanych.
  • „limit_req…” i „limit_conn…” – ochrona przed nadużyciami oraz „zjadaniem” zasobów przez jedno IP.
  • „wrk”/„ab” oraz „open_file_cache…” – pomiar i szybkie zyski przy statycznych assetach.

bezpieczeństwo: hardening i praktyczne zasady

Hardening Nginx sprowadza się do ograniczenia tego, co serwer ujawnia i akceptuje, oraz do dołożenia warstw ochronnych zmniejszających powierzchnię ataku. Najprostszy krok to „server_tokens off;”, żeby nie zdradzać wersji w nagłówkach i na stronach błędów, co nie zastępuje zabezpieczeń, ale utrudnia fingerprinting. Dobrą praktyką jest też dodanie nagłówków bezpieczeństwa, takich jak „X-Content-Type-Options: nosniff”, „X-Frame-Options: DENY/SAMEORIGIN” oraz sensowny „Content-Security-Policy”, ponieważ bezpośrednio ogranicza to ryzyko XSS i clickjackingu na warstwie reverse proxy. Jeśli masz zrobić jedną zmianę „na już”, wyłącz server_tokens i ustaw podstawowe nagłówki bezpieczeństwa.

Ataki typu brute force oraz automatyczne skany najłatwiej przyhamujesz, łącząc Nginx z fail2ban, który analizuje /var/log/nginx/access.log i blokuje IP po zbyt wielu odpowiedziach 401/403/404. To sprawdza się szczególnie wtedy, gdy obserwujesz masowe próby na endpointach w rodzaju /wp-login.php lub /admin, a jednocześnie nie chcesz przebudowywać aplikacji. Dodatkowo warto zawęzić dozwolone metody HTTP, jeśli nie są potrzebne z internetu, np. regułą „if ($request_method .~ ^(GET|POST|HEAD)$) { return 405; }”, co często bywa wymagane podczas audytów. W przypadku paneli administracyjnych najprościej odciąć dostęp po IP („allow 1.2.3.4; deny all;”) albo udostępniać je wyłącznie przez VPN.

W sytuacjach, w których potrzebujesz WAF, możesz sięgnąć po ModSecurity v3 (libmodsecurity) z Nginx, choć wdrożenie potrafi być bardziej pracochłonne, albo postawić na WAF w chmurze (np. Cloudflare) przed Nginx. Po stronie reverse proxy istotne jest również to, aby nie przepuszczać bezrefleksyjnie nagłówków takich jak X-Forwarded-Proto/Host z internetu do aplikacji, ponieważ gdy backend im ufa, atakujący może wpływać na linki, przekierowania lub logikę bezpieczeństwa. Za dobrą praktykę uchodzi uruchamianie Nginx z nieuprzywilejowanym użytkownikiem (www-data/nginx) oraz utrzymywanie minimalnych uprawnień do plików, co ogranicza skutki ewentualnego incydentu. Przy SELinux (Enforcing) pamiętaj o właściwych kontekstach dla katalogów, inaczej możesz zobaczyć 403 mimo poprawnych chmod.