Instalacja i konfiguracja nginx na Debianie i Ubuntu
Od pakietu apt po pierwszy blok server z PHP-FPM i HTTPS: która dystrybucja daje którą wersję nginx, czym różni się repozytorium nginx.org i jak pozbyć się typowych błędów.
Instalacja nginx zajmuje trzydzieści sekund. Reszta dnia schodzi na tym, że blok server nie chce zadziałać, PHP zwraca 502 albo na porcie 80 siedzi już Apache. Ten artykuł przechodzi dokładnie przez te miejsca, osobno dla Debiana 13, Debiana 12, Ubuntu 24.04 i Ubuntu 22.04, bo te cztery systemy zachowują się w kilku punktach inaczej.
Który nginx: pakiet dystrybucyjny czy oficjalne repozytorium
Pierwsza decyzja zapada jeszcze przed pierwszym poleceniem. Każda dystrybucja dostarcza zamrożoną wersję, która dostaje już tylko poprawki bezpieczeństwa. Stan na lipiec 2026 wygląda tak:
| System | nginx z pakietu dystrybucyjnego |
| Debian 13 (trixie) | 1.26.3 |
| Debian 12 (bookworm) | 1.22.1 |
| Ubuntu 24.04 LTS (noble) | 1.24.0 |
| Ubuntu 22.04 LTS (jammy) | 1.18.0 |
Na samym nginx.org w lipcu 2026 dostępne są gałęzie 1.30.4 (stable) oraz 1.31.3 (mainline). Dystans do Ubuntu 22.04 to zatem mniej więcej sześć lat rozwoju funkcji.
Weź pakiet dystrybucyjny, jeśli prowadzisz klasyczne strony WWW albo zestawy z reverse proxy i robotę ma odwalać unattended-upgrades. Weź repozytorium nginx.org, jeśli potrzebujesz HTTP/3 i QUIC (w Ubuntu 22.04 nie ma tego w ogóle, w Debianie 12 również nie), jeśli chcesz gotowych modułów dynamicznych w rodzaju nginx-module-brotli albo modułu ACME, albo jeśli na kilku dystrybucjach chcesz mieć dokładnie tę samą wersję.
O czym wiele poradników milczy: te dwa pakiety to nie ten sam program w różnych wersjach, tylko inaczej zbudowane i inaczej spakowane oprogramowanie. To najczęstszy powód, dla którego skopiowana instrukcja nie działa.
| Cecha | Pakiet Debiana/Ubuntu | Pakiet nginx.org |
| Użytkownik procesu | www-data | nginx |
| Domyślny docroot | /var/www/html | /usr/share/nginx/html |
| sites-available i sites-enabled | są | nie ma |
| /etc/nginx/snippets/ | jest | nie ma |
| Profile ufw (Nginx Full itd.) | są | nie ma |
| Moduły dynamiczne | libnginx-mod-* | nginx-module-* |
Instalacja z pakietu dystrybucyjnego
Na wszystkich czterech systemach identycznie:
apt update
apt install -y nginx
apt install -y curl
Metapakiet nginx wystarczy. nginx-full i nginx-extras nadal istnieją w Debianie 12 i 13, ale dociągają jedynie dodatkowe pakiety z modułami. Pojedyncze moduły pobierasz celowo, na przykład poleceniem apt install libnginx-mod-http-headers-more-filter.
curl stoi tu obok świadomie. Nie jest zależnością nginx i brakuje go na świeżo postawionym Debianie albo Ubuntu, także po czystej instalacji nginx. Bez tego kroku pierwsze polecenie kontrolne poniżej przerwie się komunikatem curl: command not found, a ta sama pułapka dopadnie później test nagłówka Host oraz test PHP.
Teraz część, którą większość poradników pomija: dowód, że to naprawdę działa. apt install bez komunikatu o błędzie nie dowodzi niczego.
nginx -v
systemctl is-enabled nginx
systemctl is-active nginx
curl -I http://127.0.0.1/
Spodziewane odpowiedzi to enabled, active oraz HTTP/1.1 200 OK z nagłówkiem Server, który wymienia nginx (Debian 13 odpowiada Server: nginx, Ubuntu 24.04 zaś Server: nginx/1.24.0). Dopiero wtedy serwer WWW stoi. Kto chce wiedzieć, z jakimi opcjami zbudowano pakiet, sięga po nginx -V (wielkie V); tam widać także, czy jest w nim --with-http_v3_module.
Jeśli ufw jest aktywny, brakuje jeszcze reguły firewalla. Wcześniej rzut oka na sam pakiet: na Ubuntu Server ufw jest zainstalowany fabrycznie i tylko nieaktywny, na Debianie nie ma go w ogóle. Tam pierwsze wywołanie skwitowane zostanie komunikatem ufw: command not found. Doinstaluj go więc od razu, pakiet jest dostępny na wszystkich czterech systemach:
apt install -y ufw
ufw app list
ufw allow 'Nginx Full'
Profile dostarcza pakiet nginx-common z wydania dystrybucyjnego: Nginx Full, Nginx HTTP i Nginx HTTPS, a na Debianie 13 dodatkowo Nginx QUIC. Po instalacji z repozytorium nginx.org nie pojawią się one w ufw app list w ogóle (patrz tabela wyżej).
Podłączenie oficjalnego repozytorium nginx
nginx.org obsługuje bookworm, trixie, jammy i noble. apt-key jest wycofany, klucz trafia do własnego keyringu i zostaje wskazany przez signed-by.
apt install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring
Na Ubuntu ostatni pakiet nazywa się ubuntu-keyring, a nie debian-archive-keyring. Następnie:
mkdir -p /root/.gnupg && chmod 700 /root/.gnupg
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
mkdir w pierwszym wierszu nie jest balastem. Na świeżo postawionym serwerze /root/.gnupg jeszcze nie istnieje, a gpg 2.4.7 z Debiana 13 przy dokładnie takiej kombinacji wywołań samo tego katalogu nie zakłada. Polecenie kontrolne przerywa wtedy komunikatem gpg: Fatal: /root/.gnupg: directory does not exist!, i to w środku wypisywania, czyli zanim pojawią się odciski kluczy. Kto chce je zobaczyć, musi wcześniej założyć katalog; alternatywnie wystarczy jednorazowe gpg --list-keys.
Ostatnie polecenie też nie jest ozdobnikiem, to właściwa kontrola. Pokazuje trzy klucze, tak ma być i nie jest to powód do przerywania: aktualny klucz podpisujący 8540 A6F1 8833 A80E 9C16 53A4 2FD2 1310 B49F 6B46 (signing-key-2@nginx.com), do tego 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62 oraz 9E9B E90E ACBC DE69 FE9B 204C BCDC D8A3 8D88 A2B3. Jeśli widnieją tam inne odciski, pobranie dostarczyło coś innego, niż powinno, i w tym miejscu przerywasz.
Teraz źródło pakietów. Zwróć uwagę na segment ścieżki po /packages/, bo nginx.org utrzymuje dla Debiana i Ubuntu dwa osobne drzewa katalogów. Pod /packages/debian/dists/ nie ma ani noble, ani jammy, pakiety dla Ubuntu leżą wyłącznie pod /packages/ubuntu/. Ta wersja ustawia segment sama i dlatego działa bez zmian w obu rodzinach:
OS=$(. /etc/os-release; echo $ID)
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/$OS $(lsb_release -cs) nginx" | tee /etc/apt/sources.list.d/nginx.list
$ID ma na Debianie wartość debian, a na Ubuntu ubuntu, dzięki czemu ścieżka automatycznie pasuje do dystrybucji. Jeśli zamiast tego wpiszesz na sztywno packages/debian, bo realizujesz instrukcję dla Debiana na Ubuntu, echo zapisze ten wiersz bez słowa skargi, a błąd wyjdzie dopiero przy następnym apt update:
Err: http://nginx.org/packages/debian noble Release
E: The repository 'http://nginx.org/packages/debian noble Release' does not have a Release file.
Na Ubuntu 22.04 stoi w tym miejscu jammy zamiast noble, przyczyna jest ta sama. Dla gałęzi mainline wsuwasz mainline/ przed nazwę dystrybucji. Żeby apt rzeczywiście przedkładał pakiet z nginx.org nad pakiet dystrybucyjny, potrzebne jest przypięcie (pinning), inaczej w zależności od numeru wersji wygra ten niewłaściwy:
printf 'Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n' | tee /etc/apt/preferences.d/99nginx
apt update
apt-cache policy nginx
apt install -y nginx
apt-cache policy nginx to kontrola przed instalacją: jako Candidate musi stać wersja z nginx.org (na przykład 1.30.4-1~noble), a jako priorytet 900 z pliku przypięcia. Jeśli nadal widnieje tam wersja z dystrybucji, przypięcie nie działa i zaraz zainstalujesz niewłaściwy pakiet.
Przejście z istniejącej instalacji dystrybucyjnej
Jeśli działa już pakiet dystrybucyjny, aktualizacja się nie powiedzie. Komunikat brzmi mniej więcej tak:
dpkg: error processing archive /var/cache/apt/archives/nginx_1.30.4-1~bookworm_amd64.deb (--unpack):
trying to overwrite '/etc/nginx/mime.types', which is also in package nginx-common 1.22.1-9+deb12u9
Pakiet z nginx.org nie zna nginx-common, dlatego pliki kolidują ze sobą. Czysta droga: najpierw zrób kopię zapasową konfiguracji, potem usuń pakiet dystrybucyjny, a na końcu zainstaluj od nowa.
tar czf /root/nginx-config-backup.tar.gz /etc/nginx
systemctl stop nginx
apt purge -y nginx nginx-common
apt install -y nginx
To, że tar wypisuje przy tym tar: Removing leading '/' from member names, jest normalne i nie jest błędem. Potem /etc/nginx/sites-available znika, a twoje stare vhosty leżą już tylko w archiwum. Skopiuj je do /etc/nginx/conf.d/ i nadaj im rozszerzenie .conf, inaczej nie zostaną wczytane. Zwróć przy tym uwagę na dwie rzeczy: include snippets/fastcgi-php.conf; w pakiecie nginx.org nie istnieje, a użytkownik procesu nazywa się teraz nginx, co dotyczy uprawnień do plików oraz socketów PHP-FPM.
Jak naprawdę działają sites-available i sites-enabled
Model dwóch katalogów to czysty wynalazek Debiana, sam nginx go nie zna. W /etc/nginx/sites-available/ leżą wszystkie pliki konfiguracyjne, a w /etc/nginx/sites-enabled/ symlinki do tych aktywnych. Wczytywane jest wyłącznie to, co stoi w nginx.conf pod dyrektywą include. W razie wątpliwości sprawdź to sam:
grep include /etc/nginx/nginx.conf
W pakiecie Debiana i Ubuntu stoją tam dwa wiersze, include /etc/nginx/conf.d/*.conf; oraz include /etc/nginx/sites-enabled/*;. W pakiecie nginx.org tylko ten pierwszy. Właśnie stąd bierze się klasyk: ktoś realizuje poradnik dla Ubuntu na pakiecie z nginx.org, zakłada /etc/nginx/sites-available/moja-strona, ustawia symlink, dostaje przy nginx -t czyste syntax is ok, a mimo to nic się nie dzieje. Nie ma żadnego komunikatu o błędzie, bo ten katalog po prostu nikogo nie obchodzi.
Kontrtest, który zawsze mówi prawdę, to nginx -T z wielkim T. Wypisuje pełną, rozwiniętą konfigurację, czyli dokładnie to, co nginx naprawdę widzi:
nginx -T | grep -n "server_name\|listen\|root"
Jeśli twojego server_name tam nie ma, plik nie jest wczytywany. Kropka. Dalsze grzebanie w samej konfiguracji to wtedy strata czasu.
Drugi częsty błąd to symlink prowadzący donikąd, na przykład po literówce albo dlatego, że plik docelowy zmienił nazwę:
nginx: [emerg] open() "/etc/nginx/sites-enabled/moja-strona" failed (2: No such file or directory) in /etc/nginx/nginx.conf:62
Zepsute symlinki znajdziesz poleceniem find /etc/nginx/sites-enabled/ -xtype l. Strony zresztą nigdy nie wyłącza się przez skasowanie pliku w sites-available, tylko przez usunięcie symlinka poleceniem unlink /etc/nginx/sites-enabled/moja-strona.
Pierwszy własny blok server
Zakładamy stronę statyczną. Najpierw katalog i plik testowy:
mkdir -p /var/www/przyklad/html
echo '<h1>Strona testowa KernelHost</h1>' > /var/www/przyklad/html/index.html
chown -R www-data:www-data /var/www/przyklad
W pakiecie nginx.org właścicielem jest nginx:nginx. Teraz konfiguracja, i to naprawdę jako osobny krok: plik musi istnieć, zanim wskaże na niego symlink. Załóż go edytorem albo zapisz od razu przez heredoc; w pakiecie nginx.org trafia on zamiast tego do /etc/nginx/conf.d/przyklad.conf:
cat > /etc/nginx/sites-available/przyklad <<'EOF'
server {
listen 80;
listen [::]:80;
server_name przyklad.pl www.przyklad.pl;
root /var/www/przyklad/html;
index index.html;
access_log /var/log/nginx/przyklad.access.log;
error_log /var/log/nginx/przyklad.error.log;
location / {
try_files $uri $uri/ =404;
}
}
EOF
Pojedyncze apostrofy wokół 'EOF' są ważne, inaczej shell zastąpi $uri pustką i blok będzie zepsuty już w chwili zapisu. Dopiero potem aktywuj, sprawdź i przeładuj:
ln -s /etc/nginx/sites-available/przyklad /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
Kolejność nie jest dowolna. ln zakłada symlink także wtedy, gdy plik docelowy w ogóle nie istnieje, i nie mówi ani słowa. Wychodzi to na jaw dopiero w teście:
nginx: [emerg] open() "/etc/nginx/sites-enabled/przyklad" failed (2: No such file or directory) in /etc/nginx/nginx.conf:61
Numer wiersza różni się w zależności od systemu (61 na Debianie 13, 60 na Debianie 12 i obu Ubuntu), komunikat jest identyczny. reload zostałby w takim stanie odrzucony, więc działająca konfiguracja pozostaje nietknięta. Dokładnie ten przypadek znajduje też find /etc/nginx/sites-enabled/ -xtype l.
W tym miejscu regularnie pojawiają się jeszcze trzy inne błędy.
Podwójny serwer domyślny. Kto skopiuje z poradnika default_server, mimo że domyślna strona Debiana jest nadal aktywna, dostanie:
nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/sites-enabled/przyklad:2
Albo pomiń to słowo kluczowe, albo wyłącz stronę domyślną poleceniem unlink /etc/nginx/sites-enabled/default.
Podwójna nazwa serwera. Jeśli ta sama nazwa stoi w dwóch blokach, bez słowa komentarza wygrywa ten wczytany jako pierwszy, pojawia się tylko ostrzeżenie:
nginx: [warn] conflicting server name "przyklad.pl" on 0.0.0.0:80, ignored
Zbyt długa nazwa serwera. Przy długich domenach albo wielu subdomenach:
nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 32
Pomaga server_names_hash_bucket_size 64; w bloku http pliku nginx.conf.
Dowód działania idzie bez DNS, przez nagłówek Host:
curl -H 'Host: przyklad.pl' -sS http://127.0.0.1/
Jeśli zamiast twojej strony testowej przychodzi domyślna strona Debiana, twój blok nie działa i lądujesz na serwerze domyślnym. Jeśli przychodzi twoja strona, wszystko się zgadza.
Podłączenie PHP-FPM
Tu czai się najbardziej dokuczliwa pułapka całego poradnika i kosztuje ona całe rzesze ludzi godzinę pracy. Metapakiet php ciągnie przez php8.x zależność alternatywną libapache2-mod-php8.x | php8.x-fpm | php8.x-cgi. apt wybiera zawsze pierwszą, więc apt install php niezawodnie dociąga Apache, a Apache zajmuje potem port 80. Instaluj zatem celowo:
apt install -y php-fpm php-mysql php-xml php-curl php-mbstring php-zip
To, którą wersję PHP dostaniesz i jak nazywa się socket, zależy od dystrybucji:
| System | PHP | Socket | Usługa |
| Debian 13 | 8.4 | /run/php/php8.4-fpm.sock | php8.4-fpm |
| Debian 12 | 8.2 | /run/php/php8.2-fpm.sock | php8.2-fpm |
| Ubuntu 24.04 | 8.3 | /run/php/php8.3-fpm.sock | php8.3-fpm |
| Ubuntu 22.04 | 8.1 | /run/php/php8.1-fpm.sock | php8.1-fpm |
Nie zgaduj ścieżki, tylko ją odczytaj:
ls -l /run/php/
Blok PHP wewnątrz bloku server (przykład dla Debiana 12, ścieżkę dostosuj):
index index.php index.html;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.(?!well-known).* {
deny all;
}
Na Debianie i Ubuntu pierwsze wiersze możesz zastąpić przez include snippets/fastcgi-php.conf;, w pakiecie nginx.org tego snippetu nie ma, tam potrzebujesz wersji rozpisanej. try_files $uri =404; to nie ozdobnik, tylko zabezpieczenie: bez niego wgrane pliki z doklejonym .php w ścieżce mogą zostać wykonane.
Brakuje jednak jeszcze jednego kroku, a pomija się go niemal zawsze. Testowe wywołanie za chwilę idzie przez dostarczony domyślny vhost, a w /etc/nginx/sites-available/default sekcja location ~ \.php$ jest fabrycznie w całości zakomentowana. Dopóki tak zostanie, nginx przepuszcza pliki .php bez zmian: dostajesz 200 OK z Content-Type: application/octet-stream i kod źródłowy PHP otwartym tekstem. To nie jest usterka kosmetyczna. W prawdziwym pliku stoją w tym miejscu dane logowania do bazy danych albo klucze API, a wtedy czyta je razem z tobą każdy, kto zna adres URL.
Najpierw więc uzbrój ten blok. Usuń w /etc/nginx/sites-available/default znaki komentarza przed nim albo wpisz go w wersji rozpisanej:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
Nazwa socketu zależy od dystrybucji, wymienia ją tabela wyżej: php8.4 na Debianie 13, php8.3 na Ubuntu 24.04, php8.2 na Debianie 12, php8.1 na Ubuntu 22.04. Potem sprawdź i przeładuj:
nginx -t
systemctl reload nginx
Dopiero teraz dowód ma jakąkolwiek wartość: pokaże, czy PHP naprawdę działa przez FPM, a nie tylko oferuje plik do pobrania:
printf '<?php echo "PHP ", PHP_VERSION, " via ", php_sapi_name(), "\n";' > /var/www/html/kh-check.php
curl -s http://127.0.0.1/kh-check.php
rm /var/www/html/kh-check.php
Poprawny wynik wygląda tak: PHP 8.4.23 via fpm-fcgi, na Ubuntu 24.04 odpowiednio PHP 8.3.6 via fpm-fcgi. Jeśli zamiast tego wraca kod źródłowy, location ~ \.php$ w aktywnym vhoście jeszcze nie działa, czyli nadal jest zakomentowany albo wskazuje na niewłaściwy socket. Nie zapomnij o skasowaniu pliku i nie używaj phpinfo() na serwerze dostępnym z sieci.
Jak poprawnie czytać błąd 502 Bad Gateway
502 to nie diagnoza, diagnoza stoi w /var/log/nginx/error.log. Są trzy typowe wiersze:
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) while connecting to upstream
Zła ścieżka albo FPM nie działa. Sprawdź poleceniami systemctl status php8.2-fpm i ls /run/php/. Częstym wyzwalaczem jest aktualizacja dystrybucji: po skoku z Debiana 12 na 13 stara konfiguracja wskazuje jeszcze na php8.2-fpm.sock, a zainstalowane jest 8.4.
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream
Dotyczy to niemal wyłącznie instalacji z repozytorium nginx.org. Pula FPM należy do www-data i ma tryb 0660, a nginx działa tam jako użytkownik nginx. Ustaw w /etc/php/8.2/fpm/pool.d/www.conf wartość listen.group = nginx i zrestartuj FPM.
FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream
SCRIPT_FILENAME wskazuje w pustkę. Najczęściej root stoi w bloku location zamiast w bloku server albo brakuje go zupełnie.
HTTPS z Let's Encrypt
Wersje certbota w poszczególnych dystrybucjach mocno się rozjeżdżają: Debian 13 dostarcza 4.0.0, Ubuntu 24.04 wersję 2.9.0, Debian 12 wersję 2.1.0, a Ubuntu 22.04 tylko 1.21.0. Wszystkie mówią ACMEv2, ale do nowszych funkcji, takich jak krótko żyjące certyfikaty, wersja 1.21 jest za stara. Na Ubuntu 22.04 opłaca się objazd przez snap.
apt install -y certbot python3-certbot-nginx
certbot --version
Przed wystawieniem certyfikatu muszą być spełnione trzy warunki, inaczej walidacja padnie bez użytecznego komunikatu: rekord A (i ewentualnie AAAA) wskazuje na serwer, port 80 jest osiągalny z zewnątrz i istnieje blok server z pasującym server_name. Ostatni punkt jest rozstrzygający, bo wtyczka nginx znajduje blok po server_name, a nie po nazwie pliku. Następnie:
certbot --nginx -d przyklad.pl -d www.przyklad.pl
certbot dopisuje potem do twojego pliku konfiguracyjnego: drugi blok server z listen 443 ssl, ścieżki do fullchain.pem i privkey.pem, wpis include /etc/letsencrypt/options-ssl-nginx.conf oraz przekierowanie z portu 80. To, że plik zostaje zmieniony, regularnie zaskakuje. Kto go potem nadpisze ręcznie, straci HTTPS i przy następnym przeładowaniu dostanie:
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/przyklad.pl/fullchain.pem": BIO_new_file() failed
Automatyczne odnawianie działa przez timer systemd, a nie przez cronjob. Sprawdź jedno i drugie:
systemctl list-timers | grep certbot
certbot renew --dry-run
Przebieg próbny to jedyny prawdziwy dowód, że odnowienie za dziewięćdziesiąt dni się powiedzie. Jeśli korzystasz z repozytorium nginx.org, od nginx 1.29 istnieje dodatkowo wariant zupełnie bez certbota, moduł nginx-module-acme, w którym nginx sam zamawia i odnawia certyfikaty.
nginx -t, reload i restart
Zasada jest prosta, a mimo to łamana bez przerwy: nigdy reload bez wcześniejszego nginx -t. Przy błędzie składni nginx wprawdzie odmawia przyjęcia zepsutej konfiguracji, ale przy restart stary proces jest już zakończony i strona jest offline.
nginx -t
Spodziewany wynik to dokładnie to:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
reload uruchamia nowe procesy robocze z nową konfiguracją, a starym pozwala dokończyć obsługę otwartych połączeń. Nie ginie ani jedno żądanie. restart potrzebny jest tylko wtedy, gdy zmienia się coś w samym procesie, na przykład po aktualizacji pakietu, przy zmienionym user albo przy doładowaniu dyrektyw load_module.
Gdy start się nie powiedzie, komunikat systemd jest niemal bezużyteczny:
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
Użyteczny tekst stoi o poziom niżej:
journalctl -xeu nginx.service --no-pager -n 30
Jak rozwiązać konflikt o port 80 z Apache
Najbardziej znany błąd nginx w ogóle:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Zwróć uwagę, że nginx -t tego błędu nie znajdzie, bo test nie zajmuje żadnych portów. Dopiero start się nie powiedzie. Najpierw ustal, kto trzyma port, zamiast zgadywać:
apt install -y iproute2
ss -tlnp | grep ':80'
Wynik podaje nazwę procesu wprost, typowo users:(("apache2",pid=612,fd=4)). W dziewięciu przypadkach na dziesięć Apache trafił do systemu przez apt install php albo razem z jakimś panelem. Są trzy czyste wyjścia.
Po pierwsze, wyłącz Apache. Jeśli go nie potrzebujesz, wystarczy dezaktywacja, żeby po następnym restarcie nie wrócił:
systemctl disable --now apache2
systemctl start nginx
Po drugie, usuń Apache. Uwaga: jeśli zależy od niego libapache2-mod-php, apt purge apache2 może zabrać ze sobą PHP. Sprawdź listę, którą apt pokazuje przed wykonaniem, i doinstaluj potem php-fpm.
Po trzecie, uruchom oba równolegle. Ma to sens, gdy istniejące vhosty Apache z plikami .htaccess mają działać dalej, a nginx pracuje przed nimi jako reverse proxy. W tym celu wpisujesz w /etc/apache2/ports.conf wiersz Listen 127.0.0.1:8080, zmieniasz w każdym vhoście <VirtualHost *:80> na <VirtualHost *:8080> i każesz nginx przekazywać ruch dalej:
location / {
proxy_pass http://127.0.0.1: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;
}
Żeby Apache nie widział w swoich logach już tylko 127.0.0.1, włącz w nim moduł remoteip. Bez tego kroku reguły fail2ban oparte na logach Apache są bezwartościowe, bo każde żądanie wygląda, jakby przychodziło z localhosta.
Dwa warianty tego samego błędu chętnie umykają uwadze. Jeśli w dwóch twoich własnych plikach ten sam wpis listen stoi podwójnie, nginx również zgłasza Address already in use, choć żadnego Apache w tym nie ma. A przy bind() to [::]:80 failed dotknięty jest wyłącznie IPv6, najczęściej dlatego, że drugi blok także ustawia listen [::]:80 bez ipv6only=on.
Odbiór instalacji: osiem sprawdzeń zamiast przeczucia
Zanim uznasz instalację za skończoną, przejdź przez te punkty. Każdy z nich zapobiegł już kiedyś cichej awarii.
nginx -tzgłasza test is successful.systemctl is-enabled nginxzwracaenabled, czyli usługa wróci po restarcie serwera.ss -tlnp | grep nginxpokazuje port 80 oraz, jeśli został skonfigurowany, port 443.nginx -T | grep server_namewymienia każdą domenę, która ma działać.curl -I http://127.0.0.1/zwraca kod 200 albo zamierzone przekierowanie.- Testowy plik PHP wypisuje
fpm-fcgii został potem skasowany. certbot renew --dry-runprzechodzi bez błędu./var/log/nginx/error.logpo testowym wejściu nie zawiera nowych wpisów.
Na świeżo postawionym serwerze dochodzi do tego jeszcze firewall, patrz nasz poradnik o konfiguracji ufw. Debian 10 i Ubuntu 20.04 są bez wsparcia odpowiednio od czerwca 2024 i maja 2025 i nie dostają już aktualizacji bezpieczeństwa dla nginx; aktualizacja systemu nie jest tam kwestią wygody, tylko sprawą mocno zaległą.
Co dochodzi w KernelHoście
Na serwerach root KVM i serwerach dedykowanych od KernelHost instalujesz Debiana 13, Debiana 12, Ubuntu 24.04 albo Ubuntu 22.04 wprost z panelu klienta i masz potem pełny dostęp roota, a opisane wyżej kroki działają bez zmian. Systemy stoją w centrum danych maincubes we Frankfurcie nad Menem, z certyfikatem TÜV TIER3+, podłączone przez naszą własną sieć. Ochrona DDoS działa przy tym przed serwerem, a nie dopiero w nginx: 3,2 Tbps filtrowania Arbor w czasie rzeczywistym bezpośrednio na miejscu, a w taryfach Professional dodatkowo do 17 Tbps globalnej pojemności filtrowania. Wszystko działa w modelu PrePaid, czyli bez minimalnego okresu, bez okresu wypowiedzenia i bez opłaty aktywacyjnej.
Najczęstsze pytania
Czy zainstalować nginx z pakietu dystrybucyjnego, czy z oficjalnego repozytorium nginx.org?
Dlaczego mój blok server w sites-available jest całkowicie ignorowany, mimo że nginx -t kończy się powodzeniem?
nginx nie startuje i zgłasza bind() to 0.0.0.0:80 failed (98: Address already in use). Co robić?
Której wersji PHP i którego socketu FPM potrzebuję w mojej dystrybucji?
Jak wiarygodnie sprawdzić, że nginx naprawdę działa poprawnie, a nie tylko polecenie przeszło bez błędu?
2026 KernelHost GmbH. Wszelkie prawa zastrzeżone. Ten poradnik jest chroniony prawem autorskim. Publikowanie go w innych serwisach, w całości, we fragmentach lub w zmienionej formie, wymaga naszej pisemnej zgody. Cytaty z podaniem źródła i z linkiem są jak najbardziej mile widziane.

