Nginx to szybki serwer WWW. Użyjemy go do serwowania statycznej strony albo postawimy go przed aplikacją jako reverse proxy i włączymy HTTPS z certyfikatem Let's Encrypt, który odnawia się sam.
Czego potrzebujesz
- VPS w StormHost z Ubuntu 24.04 lub Debianem 13
- Domena skierowana na adres IP VPS (rekord
A, opcjonalnieAAAA); zobacz Domena na VPS i Cloudflare - Połączenie SSH jako root (poradnik)
- Około 25 minut
Jak to działa#
Przeglądarka łączy się z portem 80 (HTTP) lub 443 (HTTPS) Twojego VPS. Na podstawie domeny w żądaniu Nginx wybiera właściwy server block (konfigurację jednej strony) i albo wysyła plik z dysku (strona statyczna), albo przekazuje żądanie aplikacji działającej na innym porcie (reverse proxy).
Certyfikat HTTPS wystawia bezpłatnie Let's Encrypt. Program Certbot weryfikuje, że kontrolujesz domenę (Let's Encrypt łączy się z portem 80 i pobiera plik kontrolny), pobiera certyfikat, konfiguruje go w Nginx i potem sam go odnawia.
Wymagania wstępne: DNS i porty#
Sprawdź, czy domena wskazuje na VPS. Na VPS uruchom:
bash getent hosts example.czZobaczysz adres IP swojego VPS (z zakładki Sieć). Jeśli nie, popraw DNS i poczekaj na propagację zmiany.
Otwórz porty 80 i 443:
- Firewall w strefie klienta (jeśli używasz trybu Blokuj wszystko poza dozwolonym): reguła Przychodzący · Zezwól · TCP · port
80,443. Poradnik: Firewall w strefie klienta. - UFW na VPS (jeśli go używasz):
ufw allow 80/tcpiufw allow 443/tcp.
- Firewall w strefie klienta (jeśli używasz trybu Blokuj wszystko poza dozwolonym): reguła Przychodzący · Zezwól · TCP · port
Cloudflare: certyfikat uzyskaj z szarą chmurką
Jeśli domeną zarządzasz w Cloudflare, na czas uzyskiwania certyfikatu zostaw rekordy ustawione na DNS only (szara chmurka). Gdy już go masz, możesz przełączyć na Proxied; zobacz Cloudflare i tryby SSL.
Instalacja Nginx#
Działa zarówno na Ubuntu 24.04, jak i na Debianie 13:
apt update
apt -y install nginxapt -y install nginx– instaluje Nginx. Usługa startuje od razu i jest włączona przy uruchomieniu VPS.
Kontrola: otwórz w przeglądarce http://<ADRES_IP_VPS>. Zamiast <ADRES_IP_VPS> wstaw adres IPv4 z zakładki Sieć (na przykład http://203.0.113.10). Zobaczysz stronę „Welcome to nginx!”.
Jak uporządkowana jest konfiguracja#
| Ścieżka | Do czego służy |
|---|---|
/etc/nginx/nginx.conf | Główna konfiguracja. Zwykle jej nie zmieniasz. Wczytuje pliki z poniższych katalogów. |
/etc/ | Konfiguracje poszczególnych stron (server blocks). Plik tutaj istnieje, ale nie jest aktywny. |
/etc/ | Dowiązania (symlinki) do plików z sites-available. Nginx wczytuje tylko to, co tu jest. |
/etc/nginx/conf.d/ | Dodatkowa konfiguracja; wczytywane są wszystkie pliki *.conf. |
/var/www/ | Zwykłe miejsce na pliki stron. |
/var/log/nginx/ | Logi: access.log (żądania) i error.log (błędy). |
Stronę włączasz więc, tworząc plik w sites-available i dowiązanie do niego w sites-enabled. Wyłączasz ją, usuwając dowiązanie.
Strona statyczna#
Strona statyczna to zestaw gotowych plików HTML, CSS, JavaScript i obrazów.
Utwórz katalog strony i stronę testową:
bash mkdir -p /var/www/example.cz/html echo '<h1>It works</h1>' > /var/www/example.cz/html/index.htmlmkdir -p– tworzy katalog łącznie z nadrzędnymi.echo '…' > …/index.html– zapisuje prostą stronę doindex.html.
Później prześlij do tego katalogu własne pliki strony przez SFTP (poradnik) albo w zakładce Pliki.
Utwórz konfigurację strony:
bash nano /etc/nginx/sites-available/example.cz/etc/ nginx/ nginxsites-available/ example.cz server { listen 80; listen [::]:80; server_name example.cz www.example.cz; root /var/www/example.cz/html; index index.html; location / { try_files $uri $uri/ =404; } }
| Dyrektywa | Co robi | Kiedy zmieniać |
|---|---|---|
listen 80; | Nasłuchuje na porcie 80 dla IPv4. | Nigdy (Certbot dodaje HTTPS). |
listen [::]:80; | To samo dla IPv6. | Usuń, jeśli VPS nie ma IPv6. |
server_name example.cz www.example.cz; | Domeny, których dotyczy ten blok. Nginx wybiera po nich stronę. | Wypisz swoje domeny oddzielone spacjami. |
root /var/ | Katalog z plikami strony. | Jeśli pliki leżą gdzie indziej. |
index index.html; | Który plik wysłać, gdy URL kończy się na /. | Dla stron PHP index.php index.html. |
location / { … } | Reguły dla wszystkich URL strony. | – |
try_files $uri $uri/ =404; | Próbuje znaleźć plik, potem katalog; jeśli nie istnieje żadne, zwraca błąd 404. | Dla aplikacji jednostronicowych (React, Vue) try_files $uri $uri/ /index.html;. |
Włącz stronę, wyłącz domyślną stronę powitalną, przetestuj konfigurację i ją wczytaj:
bash ln -s /etc/nginx/sites-available/example.cz /etc/nginx/sites-enabled/ rm /etc/nginx/sites-enabled/default nginx -t systemctl reload nginxln -s … /etc/– tworzy dowiązanie, więc Nginx zaczyna wczytywać stronę.nginx/ sites-enabled/ rm /etc/– wyłącza domyślną stronę (plik zostaje wnginx/ sites-enabled/ default sites-available).nginx -t– sprawdza składnię wszystkich konfiguracji. Musi zakończyć sięsyntax is okitest is successful.systemctl reload nginx– wczytuje nową konfigurację bez zrywania połączeń.
Kontrola: http://example.cz pokazuje nagłówek „It works” ze strony testowej.
Reverse proxy dla aplikacji#
Aplikacja (Node.js, Python, Java, kontener Dockera) zwykle działa na własnym porcie, na przykład 3000. Stawiasz przed nią Nginx jako reverse proxy: przyjmuje żądania na porcie 80/443 i przekazuje je aplikacji. Korzyści: Nginx obsługuje HTTPS, aplikacja nie musi być widoczna z internetu, a na jednym VPS może działać kilka aplikacji pod różnymi domenami.
Niech aplikacja nasłuchuje tylko na 127.0.0.1
Skonfiguruj aplikację tak, aby nasłuchiwała na 127.0.0.1:3000, nie na 0.0.0.0:3000. Jest wtedy dostępna tylko przez Nginx i nikt nie ominie go, łącząc się bezpośrednio z portem. Z Dockerem publikuj port jako 127.0.0.1:3000:3000 (zobacz Docker i Docker Compose).
Konfiguracja (zamiast strony statycznej albo jako kolejny plik dla innej domeny, na przykład app.example.cz):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name app.example.cz;
client_max_body_size 20m;
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 300s;
}
}| Dyrektywa | Co robi | Kiedy zmieniać |
|---|---|---|
map $http_upgrade $connection_upgrade { … } | Zmienna pomocnicza dla websocketów: gdy przeglądarka prosi o podniesienie połączenia, wysyłane jest upgrade, w przeciwnym razie close. Potrzebna tylko raz w całej konfiguracji (druga kopia sprawia, że nginx -t zgłasza duplikat). | Nigdy. |
client_max_body_size 20m; | Maksymalny rozmiar przesyłanego żądania. Domyślnie 1 MB; większe przesyłki kończą się błędem 413. | Zależnie od tego, co przyjmuje aplikacja. |
proxy_pass http://127.0.0.1:3000; | Dokąd przekazywane są żądania. | Port Twojej aplikacji. |
proxy_http_version 1.1; | Rozmawia z aplikacją przez HTTP/1.1. Starsze wersje Nginx używają inaczej HTTP/1.0, który nie działa z websocketami. | Nigdy. |
proxy_set_header Host $host; | Aplikacja widzi pierwotną domenę z przeglądarki. | Nigdy. |
proxy_set_header X-Real-IP …, X-Forwarded-For … | Aplikacja widzi prawdziwy adres IP odwiedzającego zamiast 127.0.0.1. | Nigdy. |
proxy_set_header X-Forwarded-Proto $scheme; | Aplikacja wie, że odwiedzający przyszedł przez HTTPS (ważne dla przekierowań i ciasteczek). | Nigdy. |
Upgrade, Connection | Przepuszczają żądania websocket (czat, aktualizacje na żywo, Socket.IO). | Nigdy. |
proxy_read_timeout 300s; | Jak długo czekać na odpowiedź aplikacji. Domyślne 60 s zamyka bezczynne websockety. | Zwiększ dla długotrwałych operacji. |
Włączenie działa tak samo jak przy stronie statycznej:
ln -s /etc/nginx/sites-available/app.example.cz /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginxAby aplikacja działała stale i startowała automatycznie po restarcie VPS, skonfiguruj ją jako usługę: Aplikacja 24/7 z systemd.
HTTPS przez Certbot#
Instalacja Certbota#
apt -y install certbot python3-certbot-nginxcertbot– klient Let's Encrypt.python3-certbot-nginx– wtyczka, która potrafi zweryfikować domenę przez Nginx i sama edytować jego konfigurację.
Certbot ze snapa
Autorzy Certbota zalecają instalację przez snap, która ma zawsze najnowszą wersję. Pakiet dystrybucji (Ubuntu 24.04 ma Certbot 2.9, Debian 13 ma 4.0) działa tak samo w normalnym użyciu i aktualizuje się z resztą pakietów. Jeśli chcesz snapa, postępuj według certbot.eff.org/instructions (otwiera się w nowym oknie) i najpierw odinstaluj pakiety apt, aby nie mieć dwóch Certbotów.
Uzyskanie certyfikatu#
certbot --nginx -d example.cz -d www.example.cz--nginx– weryfikacja przez Nginx i automatyczna edycja jego konfiguracji.-d example.cz -d www.example.cz– domeny, dla których chcesz certyfikat. Każda musi mieć rekord DNS wskazujący na VPS i być wymieniona wserver_namejakiejś strony.
Przy pierwszym uruchomieniu Certbot zadaje te pytania:
| Pytanie | Co odpowiedzieć | Dlaczego |
|---|---|---|
E-mail (Enter email address…) | Twój adres | Let's Encrypt wysyła tam ważne powiadomienia. Od 2025 nie wysyła już przypomnień o wygaśnięciu; odnawianie jest automatyczne. |
Please read the Terms of Service… Do you agree? | Y | Bez zgody na warunki nie dostaniesz certyfikatu. |
Would you be willing… to share your email address with the Electronic Frontier Foundation… | Y lub N | Tylko dla newslettera EFF; nie wpływa na certyfikat. |
Wynik: Successfully deployed certificate for example.cz to /etc/ i Congratulations! You have successfully enabled HTTPS….
Co Certbot zmienił w konfiguracji#
Otwórz konfigurację strony (nano /etc/). Linie oznaczone # managed by Certbot dodał Certbot:
| Dodana linia | Co robi |
|---|---|
listen 443 ssl; | Strona nasłuchuje także na porcie 443 z TLS. |
listen [::]:443 ssl ipv6only=on; | To samo dla IPv6. Certbot dodaje ją, gdy blok zawiera listen [::]:80. Opcję ipv6only=on (nasłuchuj na IPv6 tylko dla połączeń IPv6) dodaje tylko dla pierwszej strony na VPS; dla pozostałych linia jest bez niej. |
ssl_certificate /etc/ | Certyfikat (łącznie z pośrednimi). |
ssl_certificate_key /etc/ | Klucz prywatny certyfikatu. Nigdy nikomu go nie dawaj. |
include /etc/ | Zalecane przez Certbot ustawienia TLS (protokoły, szyfry). |
ssl_dhparam /etc/ | Parametry wymiany kluczy. |
nowy blok server z return 301 https://… | Przekierowuje HTTP na HTTPS. Certbot dodaje go automatycznie. |
Nie edytuj tych linii ręcznie, chyba że dokładnie wiesz dlaczego. Certbot opiera się na nich przy odnawianiu.
Automatyczne odnawianie#
Certyfikat Let's Encrypt jest ważny 90 dni (stan na październik 2026; Let's Encrypt stopniowo skraca ważność). Pakiet Certbota zainstalował timer, który dwa razy dziennie sprawdza certyfikaty i odnawia te, którym została mniej więcej jedna trzecia ważności. Wtyczka Nginx sama przeładowuje Nginx po odnowieniu.
Sprawdź, czy timer istnieje i czy odnawianie działa:
systemctl list-timers | grep certbot
certbot renew --dry-runsystemctl list-timers | grep certbot– wypisuje linię zcertbot.timeri czasem następnego uruchomienia.certbot renew --dry-run– próbuje odnowienia na serwerze testowym bez dotykania prawdziwych certyfikatów. Musi zakończyć sięCongratulations, all simulated renewals succeeded.
certbot certificates wypisuje Twoje certyfikaty i ich ważność.
Limity Let's Encrypt
Let's Encrypt wystawia najwyżej 5 certyfikatów dla tego samego zestawu domen w ciągu 7 dni, a po 5 nieudanych walidacjach domeny w ciągu godziny tymczasowo Cię blokuje. Podczas debugowania używaj --dry-run (serwer testowy), a prawdziwe polecenie uruchom dopiero, gdy test przejdzie.
Cloudflare i tryby SSL#
Jeśli strona działa przez proxy Cloudflare (pomarańczowa chmurka), szyfrowane są dwa połączenia: między odwiedzającym a Cloudflare oraz między Cloudflare a Twoim VPS. Drugim steruje tryb SSL/TLS w Cloudflare:
| Tryb | Połączenie z Cloudflare do VPS | Zalecenie |
|---|---|---|
| Off | Bez szyfrowania | Nie używaj. |
| Flexible | Nieszyfrowane HTTP na port 80 | Nie używaj. W połączeniu z przekierowaniem HTTPS w Nginx tworzy nieskończoną pętlę (ERR_TOO_MANY_REDIRECTS). |
| Full | Ten sam protokół co odwiedzający; przy HTTPS certyfikat VPS nie jest weryfikowany | Tylko tymczasowo. |
| Full (strict) | Ten sam protokół co odwiedzający; przy HTTPS certyfikat VPS jest weryfikowany | Zalecany. Twój certyfikat Let's Encrypt jest ważny, więc działa. |
Po przełączeniu na Proxied uruchom certbot renew --dry-run i sprawdź, czy odnawianie działa także przez proxy Cloudflare. Jeśli się nie powiedzie, upewnij się, że nie blokujesz portu 80 własnymi regułami w Cloudflare. Trwałe rozwiązanie dla zaawansowanych to weryfikacja domeny przez DNS wtyczką python3-certbot-dns-cloudflare (zobacz dokumentację Certbota (otwiera się w nowym oknie)).
Podstawowe zabezpieczenie#
Ukryj wersję Nginx. Na Debianie 13
/etc/nginx/nginx.confzawiera już domyślnieserver_tokens off;. Na Ubuntu 24.04 otwórz plik (nano /etc/nginx/nginx.conf) i w blokuhttpusuń#przed# server_tokens off;. Nginx nie zdradza wtedy swojej wersji w nagłówkach ani na stronach błędów.Nagłówki bezpieczeństwa. Możesz dodać je do bloku
serverz portem 443:nginx add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; add_header Referrer-Policy strict-origin-when-cross-origin always;X-Content-Type-Options nosniff– przeglądarka nie zgaduje typu pliku (ochrona przed zamaskowanymi skryptami).X-Frame-Options SAMEORIGIN– strony nie da się osadzić w cudzej stronie w ramce (ochrona przed clickjackingiem).Referrer-Policy strict-origin-when-cross-origin– inne strony dostają tylko domenę, nie pełny URL strony.always– nagłówek jest wysyłany także z odpowiedziami błędów.
Po każdej zmianie uruchom
nginx -tisystemctl reload nginx.
Zastanów się, zanim włączysz HSTS
Nagłówek Strict-Transport-Security (HSTS) każe przeglądarkom używać dla domeny tylko HTTPS, nawet miesiące naprzód. Jeśli HTTPS potem przestanie działać (wygasły certyfikat, przenosiny domeny), strona się nie wczyta. Włącz go dopiero, gdy HTTPS działa niezawodnie.
Weryfikacja#
curl -I http://example.cz
curl -I https://example.czcurl -I– pobiera tylko nagłówki odpowiedzi.
Pierwsze polecenie zwraca HTTP/1.1 301 Moved Permanently z nagłówkiem Location: https://example.cz/, drugie HTTP/1.1 200 OK (lub HTTP/2 200). W przeglądarce zobaczysz kłódkę obok adresu.
Rozwiązywanie problemów#
nginx -t zgłasza błąd#
Wyjście zawiera plik i numer linii, na przykład unexpected "}" in /etc/. Najczęściej brakuje średnika na końcu dyrektywy albo jest zbędny lub brakujący nawias klamrowy. Popraw linię i uruchom test ponownie. Nie restartuj Nginx z zepsutą konfiguracją; nie wystartowałby.
502 Bad Gateway#
Nginx działa, ale aplikacja za nim nie odpowiada. Sprawdź, czy aplikacja działa i nasłuchuje na porcie z proxy_pass:
ss -tlnp | grep 3000ss -tlnp– nasłuchujące porty TCP z nazwami programów.
Jeśli nic się nie wypisuje, aplikacja nie działa albo nasłuchuje gdzie indziej. Szczegóły są w /var/log/nginx/error.log (linia z connect() failed (111: Connection refused)).
504 Gateway Time-out#
Aplikacja odpowiedziała za późno. Zwiększ proxy_read_timeout albo ustal, dlaczego aplikacja jest wolna.
413 Request Entity Too Large#
Przesyłany plik jest większy niż client_max_body_size (domyślnie 1 MB). Zwiększ wartość w bloku server i przeładuj konfigurację.
Zamiast strony wyświetla się „Welcome to nginx!”#
Domyślna strona jest nadal aktywna albo domena w server_name nie zgadza się z adresem w przeglądarce. Usuń /etc/ i sprawdź server_name.
Nginx nie startuje: Address already in use#
Port 80 lub 443 zajmuje inny program, zwykle Apache. Ustal który: ss -tlnp | grep -E ':80 |:443 '. Zatrzymaj i wyłącz Apache (systemctl disable --now apache2) albo go odinstaluj.
Certbot: Could not automatically find a matching server block#
Żadna strona nie ma w server_name domeny, dla której żądasz certyfikatu. Dodaj ją do server_name, uruchom nginx -t, systemctl reload nginx i ponownie Certbota.
Certbot: Timeout during connect (likely firewall problem) lub Connection refused#
Let's Encrypt nie mógł dotrzeć do portu 80. Sprawdź DNS (getent hosts example.cz), firewall w strefie klienta i UFW. Jeśli domena ma rekord AAAA, musi wskazywać na adres IPv6 tego VPS, a strona musi nasłuchiwać także na IPv6 (listen [::]:80;).
Cloudflare: błąd 521, 522, 525 lub 526#
- 521 – VPS odrzucił połączenie: Nginx nie działa albo firewall blokuje port 443.
- 522 – przekroczony czas połączenia: VPS jest nieosiągalny albo firewall odrzuca pakiety.
- 525 – nie powiódł się handshake TLS: Nginx nie ma poprawnej konfiguracji HTTPS na porcie 443.
- 526 – certyfikat na VPS jest nieprawidłowy lub wygasł, w trybie Full (strict). Sprawdź
certbot certificates.
ERR_TOO_MANY_REDIRECTS#
Zwykle Cloudflare w trybie Flexible w połączeniu z przekierowaniem HTTPS. Przełącz Cloudflare na Full (strict).
Podsumowanie
- Strony leżą w
/etc/; aktywne są tylko te z dowiązaniem wnginx/ sites-available/ sites-enabled. - Po każdej zmianie:
nginx -tisystemctl reload nginx. - Niech aplikacja nasłuchuje na
127.0.0.1, a udostępniaj ją przez Nginx zproxy_pass, łącznie z nagłówkamiX-Forwarded-*. - Certyfikat uzyskaj przez
certbot --nginx -d …; odnawia się sam, a sprawdzisz to przezcertbot renew --dry-run. - Z Cloudflare używaj trybu Full (strict), nigdy Flexible.
Dalsza lektura#
- nginx: Beginner's Guide (otwiera się w nowym oknie)
- nginx: WebSocket proxying (otwiera się w nowym oknie)
- nginx: ngx_http_proxy_module (otwiera się w nowym oknie)
- Certbot: User Guide (otwiera się w nowym oknie)
- Let's Encrypt: Rate Limits (otwiera się w nowym oknie)
- Cloudflare: tryby szyfrowania SSL/TLS (otwiera się w nowym oknie)