nginx installeren en instellen op Debian en Ubuntu

Gepubliceerd op 17 min leestijd

Van het apt-pakket tot het eerste serverblok met PHP-FPM en HTTPS: welke nginx-versie elke distributie levert, wat er bij de nginx.org-repository anders is en hoe u de bekende foutmeldingen weer kwijtraakt.

nginx installeren duurt dertig seconden. De rest van de dag gaat op aan een serverblok dat niet aanslaat, aan PHP dat een 502 geeft of aan Apache die al op poort 80 zit. Dit artikel loopt precies die plekken langs, en wel apart voor Debian 13, Debian 12, Ubuntu 24.04 en Ubuntu 22.04, want de vier systemen gedragen zich op meerdere punten anders.

Welke nginx: distributiepakket of officiële repository

De eerste beslissing valt nog vóór het eerste commando. Elke distributie levert een bevroren versie die alleen nog beveiligingspatches krijgt. Per juli 2026 ziet dat er zo uit:

Systeemnginx uit het distributiepakket
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

Bij nginx.org zelf staan in juli 2026 de takken 1.30.4 (stable) en 1.31.3 (mainline) klaar. Het gat met Ubuntu 22.04 bedraagt daarmee ongeveer zes jaar functieontwikkeling.

Neem het distributiepakket wanneer u klassieke websites of reverse-proxy-opstellingen draait en unattended-upgrades het werk moeten doen. Neem de nginx.org-repository wanneer u HTTP/3 en QUIC nodig hebt (in Ubuntu 22.04 helemaal niet aanwezig, in Debian 12 evenmin), wanneer u kant-en-klare dynamische modules zoals nginx-module-brotli of de ACME-module wilt gebruiken, of wanneer u op meerdere distributies exact dezelfde versie wilt draaien.

Wat veel handleidingen verzwijgen: de twee pakketten zijn niet hetzelfde programma in verschillende versies, ze zijn anders gebouwd en anders verpakt. Dat is de meest voorkomende reden waarom een gekopieerde handleiding niet werkt.

KenmerkDebian/Ubuntu-pakketnginx.org-pakket
Procesgebruikerwww-datanginx
Standaard-docroot/var/www/html/usr/share/nginx/html
sites-available en sites-enabledaanwezigbestaat niet
/etc/nginx/snippets/aanwezigbestaat niet
ufw-profielen (Nginx Full enz.)aanwezigbestaat niet
Dynamische moduleslibnginx-mod-*nginx-module-*

Installatie uit het distributiepakket

Op alle vier de systemen identiek:

apt update
apt install -y nginx
apt install -y curl

Het metapakket nginx volstaat. nginx-full en nginx-extras bestaan in Debian 12 en 13 nog steeds, maar trekken alleen extra modulepakketten mee. Losse modules haalt u gericht binnen, bijvoorbeeld met apt install libnginx-mod-http-headers-more-filter.

curl staat er bewust meteen bij. Het is geen afhankelijkheid van nginx en ontbreekt op een vers opgezette Debian of Ubuntu, ook nadat nginx netjes geïnstalleerd is. Zonder deze stap breekt het eerste controlecommando hieronder af met curl: command not found, en dezelfde valkuil treft later de host-headertest en de PHP-test.

Nu het deel dat de meeste handleidingen weglaten: het bewijs dat het werkelijk draait. Een apt install zonder foutmelding bewijst helemaal niets.

nginx -v
systemctl is-enabled nginx
systemctl is-active nginx
curl -I http://127.0.0.1/

Verwacht worden enabled, active en een HTTP/1.1 200 OK met een Server-header die nginx noemt (Debian 13 antwoordt met Server: nginx, Ubuntu 24.04 met Server: nginx/1.24.0). Pas dan staat de webserver. Wie wil weten met welke opties het pakket gebouwd is, gebruikt nginx -V (hoofdletter V), daar staat ook of --with-http_v3_module erbij zit.

Is ufw actief, dan ontbreekt nu nog de firewallregel. Eerst een blik op het pakket zelf: op Ubuntu Server is ufw standaard geïnstalleerd en alleen inactief, op Debian zit het er helemaal niet bij. Daar antwoordt de eerste aanroep anders met ufw: command not found. Installeer het dus mee, het pakket bestaat op alle vier de systemen:

apt install -y ufw
ufw app list
ufw allow 'Nginx Full'

De profielen komen mee met het pakket nginx-common uit het distributiepakket: Nginx Full, Nginx HTTP en Nginx HTTPS, op Debian 13 daarnaast Nginx QUIC. Installeert u uit de nginx.org-repository, dan duiken ze in ufw app list helemaal niet op (zie de tabel hierboven).

De officiële nginx-repository toevoegen

nginx.org ondersteunt bookworm, trixie, jammy en noble. apt-key is afgeschaft, de sleutel hoort in een eigen keyring en wordt via signed-by aangehaald.

apt install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring

Op Ubuntu heet het laatste pakket ubuntu-keyring in plaats van debian-archive-keyring. Daarna:

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

De mkdir op de eerste regel is geen ballast. Op een vers opgezette server bestaat /root/.gnupg nog niet, en de gpg 2.4.7 uit Debian 13 maakt die map bij precies deze combinatie van aanroepen niet zelf aan. Het controlecommando breekt dan af met gpg: Fatal: /root/.gnupg: directory does not exist!, en wel midden in de uitvoer, dus voordat de vingerafdrukken verschijnen. Wie ze wil zien, moet de map vooraf aanmaken. Een eenmalige gpg --list-keys volstaat ook.

Het laatste commando is evenmin versiering, het is de eigenlijke tegencontrole. Het toont drie sleutels, dat hoort zo en is geen reden om af te breken: de actuele handtekeningsleutel 8540 A6F1 8833 A80E 9C16 53A4 2FD2 1310 B49F 6B46 (signing-key-2@nginx.com), daarnaast 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62 en 9E9B E90E ACBC DE69 FE9B 204C BCDC D8A3 8D88 A2B3. Staan er andere vingerafdrukken, dan heeft de download iets anders geleverd dan verwacht en breekt u hier af.

Nu de pakketbron. Let op het padsegment achter /packages/, want nginx.org onderhoudt voor Debian en Ubuntu twee gescheiden mappenbomen. Onder /packages/debian/dists/ bestaat noch noble noch jammy, de Ubuntu-pakketten staan uitsluitend onder /packages/ubuntu/. Deze versie zet het segment zelf en draait daarom op beide families ongewijzigd:

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 is op Debian debian en op Ubuntu ubuntu, daarmee past het pad automatisch bij de distributie. Vult u in plaats daarvan vast packages/debian in, omdat u een Debian-handleiding op een Ubuntu afwerkt, dan schrijft echo die regel zonder klagen weg en komt de fout pas bij de volgende 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.

Op Ubuntu 22.04 staat daar jammy in plaats van noble, de oorzaak is dezelfde. Voor de mainline-tak zet u mainline/ vóór de distributienaam. Om apt het nginx.org-pakket ook werkelijk boven het distributiepakket te laten kiezen, is pinning nodig, anders wint afhankelijk van het versienummer het verkeerde:

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 is de controle vóór de installatie: bij Candidate moet de nginx.org-versie staan (bijvoorbeeld 1.30.4-1~noble) en als prioriteit de 900 uit het pin-bestand. Staat daar nog steeds de versie van de distributie, dan grijpt de pinning niet aan en installeert u meteen het verkeerde pakket.

Overstappen vanaf een bestaande distributie-installatie

Draait het distributiepakket al, dan mislukt de upgrade. De melding luidt ongeveer zo:

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

Het nginx.org-pakket kent geen nginx-common, daarom botsen de bestanden. De schone weg: eerst de configuratie veiligstellen, dan het distributiepakket verwijderen, dan opnieuw installeren.

tar czf /root/nginx-config-backup.tar.gz /etc/nginx
systemctl stop nginx
apt purge -y nginx nginx-common
apt install -y nginx

Dat tar daarbij tar: Removing leading '/' from member names meldt, is normaal en geen fout. Daarna is /etc/nginx/sites-available verdwenen en staan uw oude vhosts alleen nog in het tar-archief. Kopieer ze naar /etc/nginx/conf.d/ en geef ze de extensie .conf, anders worden ze niet geladen. Let daarbij op twee dingen: include snippets/fastcgi-php.conf; bestaat in het nginx.org-pakket niet, en de procesgebruiker heet nu nginx, wat gevolgen heeft voor bestandsrechten en PHP-FPM-sockets.

sites-available en sites-enabled goed begrijpen

Het model met twee mappen is een pure Debian-uitvinding, nginx zelf kent het niet. In /etc/nginx/sites-available/ staan alle configuratiebestanden, in /etc/nginx/sites-enabled/ staan symlinks naar de actieve daarvan. Geladen wordt alleen wat via include in nginx.conf staat. Controleer dat bij twijfel zelf:

grep include /etc/nginx/nginx.conf

In het Debian- en Ubuntu-pakket staan daar twee regels, include /etc/nginx/conf.d/*.conf; en include /etc/nginx/sites-enabled/*;. In het nginx.org-pakket staat alleen de eerste. Precies daaruit ontstaat de klassieker: iemand volgt een Ubuntu-handleiding op een nginx.org-pakket, maakt /etc/nginx/sites-available/mijn-site aan, zet de symlink, krijgt bij nginx -t een keurig syntax is ok, en toch gebeurt er niets. Een foutmelding blijft uit, want die map interesseert eenvoudigweg niemand.

De tegentest die altijd de waarheid vertelt, is nginx -T met een hoofdletter T. Die geeft de volledig uitgewerkte configuratie weer, dus precies wat nginx werkelijk ziet:

nginx -T | grep -n "server_name\|listen\|root"

Duikt uw server_name daar niet op, dan wordt het bestand niet ingelezen. Punt. Verder zoeken in de configuratie zelf is dan tijdverspilling.

De tweede veelgemaakte fout is een symlink die nergens naartoe wijst, bijvoorbeeld na een typefout of doordat het doelbestand hernoemd is:

nginx: [emerg] open() "/etc/nginx/sites-enabled/mijn-site" failed (2: No such file or directory) in /etc/nginx/nginx.conf:62

Kapotte symlinks vindt u met find /etc/nginx/sites-enabled/ -xtype l. Een site schakelt u overigens nooit uit door hem in sites-available te verwijderen, maar door de symlink weg te halen met unlink /etc/nginx/sites-enabled/mijn-site.

Het eerste eigen serverblok

We zetten een statische site op. Eerst de map en een testbestand:

mkdir -p /var/www/voorbeeld/html
echo '<h1>KernelHost testpagina</h1>' > /var/www/voorbeeld/html/index.html
chown -R www-data:www-data /var/www/voorbeeld

Bij het nginx.org-pakket luidt de eigenaar nginx:nginx. Nu de configuratie, en wel echt als aparte stap: het bestand moet bestaan voordat de symlink ernaar wijst. Maak het aan met een editor of schrijf het rechtstreeks weg met een heredoc. In het nginx.org-pakket gaat het in plaats daarvan naar /etc/nginx/conf.d/voorbeeld.conf:

cat > /etc/nginx/sites-available/voorbeeld <<'EOF'
server {
    listen 80;
    listen [::]:80;

    server_name voorbeeld.nl www.voorbeeld.nl;
    root /var/www/voorbeeld/html;
    index index.html;

    access_log /var/log/nginx/voorbeeld.access.log;
    error_log  /var/log/nginx/voorbeeld.error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}
EOF

De enkele aanhalingstekens om 'EOF' zijn belangrijk, anders vervangt de shell $uri door niets en is het blok al bij het opslaan kapot. Pas daarna activeren, testen en opnieuw laden:

ln -s /etc/nginx/sites-available/voorbeeld /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

De volgorde is niet willekeurig. ln maakt de symlink ook aan wanneer het doelbestand helemaal niet bestaat, zonder een woord te zeggen. Dat komt pas bij de test aan het licht:

nginx: [emerg] open() "/etc/nginx/sites-enabled/voorbeeld" failed (2: No such file or directory) in /etc/nginx/nginx.conf:61

Het regelnummer verschilt per systeem (61 op Debian 13, 60 op Debian 12 en de beide Ubuntu-versies), de melding is identiek. Een reload zou in deze toestand geweigerd worden, de draaiende configuratie blijft dus onaangeroerd. Precies dit geval vindt ook find /etc/nginx/sites-enabled/ -xtype l.

Drie andere fouten duiken op dit punt telkens weer op.

Dubbele default-server. Wie default_server uit een handleiding meekopieert terwijl de Debian-standaardpagina nog actief is, krijgt:

nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/sites-enabled/voorbeeld:2

Laat het sleutelwoord weg of schakel de standaardpagina uit met unlink /etc/nginx/sites-enabled/default.

Dubbele servernaam. Staat dezelfde naam in twee blokken, dan wint zonder commentaar het blok dat als eerste geladen is, er komt alleen een waarschuwing:

nginx: [warn] conflicting server name "voorbeeld.nl" on 0.0.0.0:80, ignored

Te lange servernaam. Bij lange domeinen of veel subdomeinen:

nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 32

De oplossing is server_names_hash_bucket_size 64; in het http-blok van nginx.conf.

Het functiebewijs loopt zonder DNS via de host-header:

curl -H 'Host: voorbeeld.nl' -sS http://127.0.0.1/

Komt de Debian-standaardpagina terug in plaats van uw testpagina, dan grijpt uw blok niet aan en belandt u in de default-server. Komt uw pagina terug, dan klopt het.

PHP-FPM aansluiten

Hier ligt de vervelendste valkuil van de hele handleiding, en hij kost mensen bij bosjes een uur. Het metapakket php hangt via php8.x aan het alternatief libapache2-mod-php8.x | php8.x-fpm | php8.x-cgi. apt kiest altijd het eerste, dus installeert apt install php steevast Apache mee, en Apache bezet daarna poort 80. Installeer daarom gericht:

apt install -y php-fpm php-mysql php-xml php-curl php-mbstring php-zip

Welke PHP-versie u krijgt en hoe de socket heet, hangt van de distributie af:

SysteemPHPSocketService
Debian 138.4/run/php/php8.4-fpm.sockphp8.4-fpm
Debian 128.2/run/php/php8.2-fpm.sockphp8.2-fpm
Ubuntu 24.048.3/run/php/php8.3-fpm.sockphp8.3-fpm
Ubuntu 22.048.1/run/php/php8.1-fpm.sockphp8.1-fpm

Gok het pad niet, lees het af:

ls -l /run/php/

Het PHP-blok in de server (voorbeeld Debian 12, pad aanpassen):

    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;
    }

Op Debian en Ubuntu kunt u de eerste regels vervangen door include snippets/fastcgi-php.conf;. In het nginx.org-pakket bestaat dit snippet niet, daar hebt u de uitgeschreven versie nodig. Het try_files $uri =404; is geen stijlmiddel, het voorkomt dat geüploade bestanden met een aangehangen .php in het pad worden uitgevoerd.

Eén stap ontbreekt echter nog, en die wordt bijna altijd overgeslagen. De testaanroep zo dadelijk loopt via de meegeleverde default-vhost, en in /etc/nginx/sites-available/default is het onderdeel location ~ \.php$ standaard volledig uitgecommentarieerd. Zolang dat zo blijft, geeft nginx .php-bestanden ongewijzigd door: u krijgt een 200 OK met Content-Type: application/octet-stream en de PHP-broncode in leesbare vorm. Dat is geen schoonheidsfoutje. In een echt bestand staan op die plek databasegegevens of API-sleutels, en die leest dan iedereen mee die de URL kent.

Zet het blok dus eerst scherp. Haal in /etc/nginx/sites-available/default de commentaartekens ervoor weg of vul het uitgeschreven in:

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
    }

De socketnaam richt zich naar de distributie, de tabel hierboven noemt hem: php8.4 op Debian 13, php8.3 op Ubuntu 24.04, php8.2 op Debian 12, php8.1 op Ubuntu 22.04. Daarna testen en opnieuw laden:

nginx -t
systemctl reload nginx

Pas nu zegt het bewijs werkelijk iets, namelijk dat PHP echt via FPM draait en niet alleen het downloaden van het bestand wordt aangeboden:

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

Goed is een uitvoer als PHP 8.4.23 via fpm-fcgi, op Ubuntu 24.04 navenant PHP 8.3.6 via fpm-fcgi. Komt in plaats daarvan de broncode terug, dan grijpt location ~ \.php$ in de actieve vhost nog niet aan, staat dus nog steeds in commentaar of wijst naar de verkeerde socket. Vergeet het verwijderen niet, en gebruik geen phpinfo() op een server die van buitenaf bereikbaar is.

502 Bad Gateway goed lezen

Een 502 is geen diagnose, de diagnose staat in /var/log/nginx/error.log. Er zijn drie typische regels:

connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) while connecting to upstream

Verkeerd pad, of FPM draait niet. Controleer met systemctl status php8.2-fpm en ls /run/php/. Een veelvoorkomende aanleiding is een distributie-upgrade: na de sprong van Debian 12 naar 13 wijst de oude configuratie nog naar php8.2-fpm.sock, maar geïnstalleerd is 8.4.

connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream

Dit treft vrijwel uitsluitend installaties uit de nginx.org-repository. De FPM-pool is eigendom van www-data en heeft modus 0660, maar nginx draait daar als gebruiker nginx. Zet in /etc/php/8.2/fpm/pool.d/www.conf de waarde listen.group = nginx en herstart FPM.

FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream

SCRIPT_FILENAME wijst nergens naartoe. Meestal staat root in het location-blok in plaats van in het server-blok, of ontbreekt het helemaal.

HTTPS met Let's Encrypt

De certbot-versies van de distributies liggen ver uit elkaar: Debian 13 levert 4.0.0, Ubuntu 24.04 de 2.9.0, Debian 12 de 2.1.0 en Ubuntu 22.04 slechts 1.21.0. Alle spreken ACMEv2, maar voor nieuwere functies zoals kortlevende certificaten is de 1.21 te oud. Op Ubuntu 22.04 loont de omweg via snap.

apt install -y certbot python3-certbot-nginx
certbot --version

Vóór het uitgeven moeten drie voorwaarden vervuld zijn, anders mislukt de validatie zonder bruikbare melding: het A-record (en eventueel AAAA) wijst naar de server, poort 80 is van buitenaf bereikbaar, en er bestaat een serverblok met een passende server_name. Dat laatste punt is doorslaggevend, want de nginx-plug-in vindt het blok via server_name, niet via de bestandsnaam. Daarna:

certbot --nginx -d voorbeeld.nl -d www.voorbeeld.nl

certbot schrijft daarop in uw configuratiebestand: een tweede serverblok met listen 443 ssl, de paden naar fullchain.pem en privkey.pem, een include /etc/letsencrypt/options-ssl-nginx.conf en een omleiding vanaf poort 80. Dat het bestand daarbij wordt gewijzigd, verrast met enige regelmaat. Wie het daarna met de hand overschrijft, verliest HTTPS en krijgt bij de volgende reload:

nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/voorbeeld.nl/fullchain.pem": BIO_new_file() failed

De automatische verlenging loopt via een systemd-timer, niet via een cronjob. Controleer beide:

systemctl list-timers | grep certbot
certbot renew --dry-run

De proefrun is het enige echte bewijs dat de verlenging over negentig dagen lukt. Gebruikt u de nginx.org-repository, dan bestaat er sinds nginx 1.29 met nginx-module-acme bovendien een variant helemaal zonder certbot, waarbij nginx de certificaten zelf aanvraagt en verlengt.

nginx -t, reload en restart

De regel is simpel en wordt toch voortdurend geschonden: nooit reload zonder eerst nginx -t. Bij een syntaxfout weigert nginx weliswaar de kapotte configuratie over te nemen, maar bij een restart is het oude proces al beëindigd en staat de site offline.

nginx -t

Verwacht wordt precies dit:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

reload start nieuwe workers met de nieuwe configuratie en laat de oude hun openstaande verbindingen netjes afhandelen. Geen enkele aanvraag gaat verloren. restart hebt u alleen nodig wanneer er iets aan het proces zelf verandert, bijvoorbeeld na een pakketupdate, bij een gewijzigde user of bij het inladen van load_module-directives.

Mislukt de start, dan is de uitvoer van systemd vrijwel nutteloos:

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.

De bruikbare tekst staat een niveau dieper:

journalctl -xeu nginx.service --no-pager -n 30

Poortconflict met Apache op poort 80 oplossen

De bekendste nginx-fout die er is:

nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)

Let erop dat nginx -t deze fout niet vindt, de test bindt geen poorten. Pas de start mislukt. Zoek eerst uit wie de poort vasthoudt, in plaats van te gokken:

apt install -y iproute2
ss -tlnp | grep ':80'

De uitvoer noemt het proces bij naam, typisch users:(("apache2",pid=612,fd=4)). In negen van de tien gevallen is Apache via apt install php of via een paneel meegekomen. Er zijn drie nette uitwegen.

Ten eerste: Apache uitschakelen. Hebt u hem niet nodig, dan volstaat het uitschakelen, zodat hij na de volgende herstart niet terugkomt:

systemctl disable --now apache2
systemctl start nginx

Ten tweede: Apache verwijderen. Let op: hangt libapache2-mod-php eraan, dan neemt apt purge apache2 onder omstandigheden PHP mee. Controleer de lijst die apt vóór de uitvoering toont en installeer daarna php-fpm na.

Ten derde: beide naast elkaar draaien. Dat is zinvol wanneer bestaande Apache-vhosts met .htaccess moeten blijven werken en nginx daarvoor als reverse proxy staat. Zet daarvoor in /etc/apache2/ports.conf een Listen 127.0.0.1:8080, wijzig in elke vhost <VirtualHost *:80> in <VirtualHost *:8080> en laat nginx doorsturen:

    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;
    }

Zodat Apache in zijn logbestanden niet meer alleen 127.0.0.1 ziet, activeert u daar de module remoteip. Zonder deze stap zijn fail2ban-regels op Apache-logbestanden waardeloos, omdat elke aanvraag van localhost lijkt te komen.

Twee varianten van dezelfde fout worden graag over het hoofd gezien. Staat in twee van uw eigen bestanden dezelfde listen-regel dubbel, dan meldt nginx eveneens Address already in use, terwijl er helemaal geen Apache in het spel is. En bij bind() to [::]:80 failed is alleen IPv6 getroffen, meestal doordat een tweede blok eveneens listen [::]:80 zonder ipv6only=on zet.

De oplevering: acht controles in plaats van onderbuikgevoel

Voordat u een installatie als afgerond beschouwt, loopt u deze punten langs. Elk punt afzonderlijk heeft al eens een stille uitval voorkomen.

  1. nginx -t meldt test is successful.
  2. systemctl is-enabled nginx geeft enabled terug, de service komt na een herstart dus vanzelf terug.
  3. ss -tlnp | grep nginx toont poort 80 en, indien ingericht, poort 443.
  4. nginx -T | grep server_name somt elk domein op dat moet draaien.
  5. curl -I http://127.0.0.1/ levert een 200 op of een bedoelde omleiding.
  6. Het PHP-testbestand geeft fpm-fcgi weer en is daarna verwijderd.
  7. certbot renew --dry-run loopt foutloos door.
  8. /var/log/nginx/error.log bevat na een testtoegang geen nieuwe regels.

Op een vers opgezette server hoort daarna ook de firewall erbij, zie daarvoor onze handleiding over het instellen van ufw. Debian 10 en Ubuntu 20.04 zijn sinds juni 2024 respectievelijk mei 2025 zonder ondersteuning en krijgen geen nginx-beveiligingsupdates meer. Een upgrade is daar geen kwestie van comfort, maar gewoon achterstallig.

Wat KernelHost daaraan toevoegt

Op de KVM-rootservers en dedicated servers van KernelHost installeert u Debian 13, Debian 12, Ubuntu 24.04 of Ubuntu 22.04 rechtstreeks vanuit het klantenpaneel en hebt u daarna volledige roottoegang, de hierboven beschreven stappen werken ongewijzigd. De systemen staan in het datacenter maincubes in Frankfurt am Main, TÜV-gecertificeerd volgens TIER3+, aangesloten via ons eigen netwerk. De DDoS-bescherming grijpt daarbij vóór de server in en niet pas in nginx: 3,2 Tbps Arbor-realtimefiltering rechtstreeks op de locatie, in de Professional-tarieven daarnaast tot 17 Tbps globale filtercapaciteit. Alles loopt op PrePaid-basis, dus zonder minimale looptijd, zonder opzegtermijn en zonder installatiekosten.

Veelgestelde vragen

Kan ik nginx beter uit het distributiepakket of uit de officiële nginx.org-repository installeren?
Voor klassieke websites en reverse-proxy-opstellingen volstaat het distributiepakket ruimschoots, het krijgt gedurende de hele looptijd beveiligingsupdates zonder dat u er iets voor hoeft te doen. De nginx.org-repository loont wanneer u HTTP/3 en QUIC nodig hebt (in Debian 12 en Ubuntu 22.04 niet aanwezig), kant-en-klare dynamische modules zoals Brotli of de ACME-module wilt inzetten, of op meerdere distributies exact dezelfde versie wilt draaien. Houd er rekening mee dat het nginx.org-pakket anders is opgebouwd: het draait als gebruiker nginx in plaats van www-data en kent noch sites-available noch de map snippets.
Waarom wordt mijn serverblok in sites-available volledig genegeerd, terwijl nginx -t wel slaagt?
Of de symlink in sites-enabled ontbreekt, of uw nginx.conf laadt die map helemaal niet. Dat laatste is bij het pakket van nginx.org altijd het geval, daar bestaat alleen include /etc/nginx/conf.d/*.conf. nginx -t controleert uitsluitend de bestanden die werkelijk zijn ingebonden, daarom blijft de test groen. De waarheid toont nginx -T met een hoofdletter T: duikt uw server_name in die uitvoer niet op, dan wordt het bestand niet gelezen. Bij het nginx.org-pakket verplaatst u de configuratie naar /etc/nginx/conf.d/ en geeft u die de extensie .conf.
nginx start niet en meldt bind() to 0.0.0.0:80 failed (98: Address already in use). Wat nu?
Een ander proces houdt poort 80 vast, meestal Apache. Zoek het op met ss -tlnp | grep ':80', de uitvoer noemt procesnaam en PID. Heel vaak is Apache ongemerkt via apt install php in het systeem beland, omdat het PHP-metapakket libapache2-mod-php als eerste alternatief oplost. Daarna hebt u drie mogelijkheden: Apache met systemctl disable --now apache2 blijvend uitschakelen, hem via apt purge verwijderen (controleer daarbij of PHP wordt meegenomen), of hem in /etc/apache2/ports.conf naar 127.0.0.1:8080 verhuizen en nginx ervoor zetten als reverse proxy. Belangrijk: nginx -t vindt deze fout niet, want de configuratietest bindt geen poorten.
Welke PHP-versie en welke FPM-socket heb ik op mijn distributie nodig?
Debian 13 levert PHP 8.4 met /run/php/php8.4-fpm.sock, Debian 12 PHP 8.2 met php8.2-fpm.sock, Ubuntu 24.04 PHP 8.3 met php8.3-fpm.sock en Ubuntu 22.04 PHP 8.1 met php8.1-fpm.sock. Gok het pad niet, maar lees het af met ls -l /run/php/. Installeer gericht php-fpm en niet het metapakket php, anders trekt apt Apache mee het systeem in. Na een distributie-upgrade moet u het socketpad in uw nginx-configuratie aanpassen, anders krijgt u een 502 met de logregel connect() to unix:/run/php/... failed (2: No such file or directory).
Hoe stel ik betrouwbaar vast dat nginx werkelijk goed draait en niet alleen het commando is doorgelopen?
Controleer vijf dingen: nginx -t meldt test is successful, systemctl is-enabled nginx geeft enabled terug (anders ontbreekt de webserver na de volgende herstart), ss -tlnp | grep nginx toont de verwachte poorten, nginx -T | grep server_name somt elk domein op dat moet draaien, en curl -I http://127.0.0.1/ levert een 200 op (curl is geen afhankelijkheid van nginx en ontbreekt op een vers geïnstalleerd systeem, haal het vooraf binnen met apt install -y curl). Voor PHP maakt u kort een bestand met php_sapi_name() aan, de uitvoer moet fpm-fcgi luiden, daarna verwijdert u het weer. Komt in plaats van die uitvoer de PHP-broncode terug, dan staat het PHP-blok in de actieve vhost nog in commentaar, precies zoals standaard in /etc/nginx/sites-available/default. Voor HTTPS is certbot renew --dry-run het enige echte bewijs dat de verlenging over negentig dagen werkt.

nginx Debian Ubuntu Webserver PHP-FPM HTTPS Let's Encrypt Linux-beheer