← schwarz.cc --:--:--
05 / BLOG POST

nginx ist der geilste Webserver der Welt

TL;DR Igor Sysoev hat 2004 nginx gebaut weil Apache zu beschissen war um 10.000 gleichzeitige Verbindungen zu handeln. Heute läuft ein Drittel des gesamten Internets drauf. Auf einem $4-Server. Sprich darüber.

der C10k-problem – warum überhaupt nginx?

2004 hatte das Internet ein Problem: Apache ist für jede eingehende Verbindung einen neuen Thread oder Prozess geforkt. Bei 10.000 gleichzeitigen Verbindungen bedeutet das 10.000 Threads. Jeder Thread frisst RAM. Server kotzt ab. Website tot.

Igor Sysoev, ein russischer Entwickler, hat sich das angeschaut und gedacht: das ist strukturell kaputt. Seine Lösung: event-driven, asynchronous, non-blocking I/O. Ein Master-Prozess, ein paar Worker-Prozesse, kein Thread pro Connection. Fertig.

1thread
pro worker
10k+
connections gleichzeitig
2.5MB
RAM im idle
34%
des gesamten webs

wie nginx requests verarbeitet

Apache: 1 Connection = 1 Thread = 1-10 MB RAM. Bei 1000 Usern gleichzeitig = 1-10 GB RAM allein für die Threads. Bevor ein einziges Byte deiner index.html ausgeliefert wurde.

nginx: 1 Worker-Prozess handelt tausende Connections über einen Event Loop. Warten auf Festplatte? Nächste Connection. Warten auf Netzwerk? Nächste Connection. Der Worker steht nie rum.

# So sieht nginx intern aus (vereinfacht)

master process    # liest config, startet workers, bleibt am Leben
 └── worker 0      # handelt ALLE connections auf CPU-Core 0
 └── worker 1      # handelt ALLE connections auf CPU-Core 1
 └── worker 2      # usw.

# Pro Worker: ein einziger Thread, ein Event Loop
# kein forken, kein spawnen, kein Speicher-Orgien

die config die einfach Sinn macht

Apache-Config ist XML-artiger Brei aus den 90ern. nginx-Config ist so lesbar dass man sie beim ersten Mal fast versteht. Fast.

# Das hier ist eine vollständige, produktionsreife nginx-Config.
# Apache braucht für dasselbe 3x so viele Zeilen.

server {
    listen       443 ssl;
    server_name  schwarz.cc www.schwarz.cc;
    root         /var/www/schwarz.cc;
    index        index.html;

    # SSL via Let's Encrypt
    ssl_certificate     /etc/letsencrypt/live/schwarz.cc/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/schwarz.cc/privkey.pem;

    # Gzip – weil du nicht im Jahr 2000 lebst
    gzip              on;
    gzip_types        text/html text/css application/javascript;

    # Browser-Caching für statische Files
    location ~* \.(css|js|png|jpg|ico)$ {
        expires 30d;
        add_header Cache-Control "public";
    }

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

# HTTP → HTTPS Redirect
server {
    listen      80;
    server_name schwarz.cc www.schwarz.cc;
    return      301 https://$host$request_uri;
}

nginx kann noch viel mehr Scheiße

01
Reverse Proxy Hast du eine Node/Python/Go-App die auf Port 3000 läuft? nginx nimmt den Traffic rein und leitet ihn weiter. SSL-Terminierung, Absicherung, Caching – alles nginx, nicht deine App. Mehr dazu gleich unten.
02
Load Balancer Mehrere Backend-Server? upstream-Block definieren, nginx verteilt automatisch. Round-Robin, least-connections, IP-hash – alles drin.
03
Rate Limiting Jemand hammert deine API mit 10.000 Requests/Sekunde? limit_req_zone – drei Zeilen Config, DDoS gebremst. Kein Code.
04
HTTP/2 und HTTP/3 Einfach listen 443 ssl http2 schreiben. Multiplexing, Header-Compression, alles gratis. Apache hat dafür ein Modul das sich manchmal selbst hasst.
05
Statische Files ausliefern mit Lichtgeschwindigkeit nginx kann direkt aus dem Kernel-Cache bedienen via sendfile on. Zero-copy. Die Datei geht vom Kernel-Buffer direkt ans Netzwerk-Interface ohne einmal in den Userspace zu wechseln.

reverse proxy – das killer-feature im detail

Das ist das Feature warum nginx auf jedem Produktions-Server läuft auf dem irgendwas Ernstes läuft. Das Prinzip: deine Applikation läuft intern auf irgendeinem Port, hört nur auf localhost, hat keinen direkten Kontakt mit dem bösen Internet. nginx sitzt davor, nimmt alles rein und entscheidet was durchkommt.

Klassisches Beispiel aus der Praxis: alter Odoo-Server. Odoo hat einen eingebauten Webserver der von aussen erreichbar ist – und der hatte lange eine nette Funktion: über eine bestimmte URL konnte man als Admin die komplette Datenbank löschen. Direkt. Im Browser. Ohne weitere Bestätigung. Danke, Odoo. nginx davor, Route blocken, Problem weg. Kein Odoo-Update nötig, kein Fummeln am Applikationscode.

REAL WORLD HORROR https://deinodoo.ch/web/database/drop — öffentlich erreichbar, ein Button, Datenbank weg. Produktionsdaten. Weg. Für immer. Das ist kein hypothetisches Szenario, das ist ein bekanntes Problem das Leute tatsächlich getroffen hat. nginx hätte das in 3 Zeilen verhindert.
# Basis Reverse Proxy – App läuft auf Port 8069 (Odoo default)
server {
    listen      443 ssl http2;
    server_name odoo.firma.ch;

    # SSL wie gehabt
    ssl_certificate     /etc/letsencrypt/live/odoo.firma.ch/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/odoo.firma.ch/privkey.pem;

    # DIESE ROUTEN BLOCKIEREN – Datenbank-Manager von aussen sperren
    location ~* ^/web/database {
        deny all;
        return 404;  # 403 verrät dass die Route existiert. 404 ist stealthier.
    }

    # Alles andere an Odoo weitergeben
    location / {
        proxy_pass         http://127.0.0.1:8069;
        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;
    }
}

Der wichtige Teil: proxy_pass http://127.0.0.1:8069 – die App hört nur auf localhost, ist also vom Internet direkt gar nicht erreichbar. Firewall-seitig ist Port 8069 zu. Nur nginx darf rein. Der Rest der Welt sieht nur Port 443.

# App nur auf localhost binden – Beispiel Odoo (/etc/odoo/odoo.conf)
xmlrpc_interface = 127.0.0.1
netrpc_interface = 127.0.0.1

# AWS Security Group: nur 80 + 443 offen lassen, App-Port zu
# Oder lokal via ufw:
sudo ufw allow 80,443/tcp
sudo ufw deny 8069
✓ Jetzt kann jemand /web/database/drop aufrufen so oft er will. nginx gibt 404 zurück. Odoo bekommt davon nichts mit. Datenbank lebt. Chef ist glücklich.

Das gleiche Prinzip funktioniert für jeden Applikationsserver: Node, Python/Django, Rails, Laravel, Java – egal. Die App kennt nur localhost, nginx ist der einzige Türsteher. Bonus: SSL, Gzip, Rate Limiting, Logging und Header-Sanitizing für jede App, ohne eine Zeile Applikationscode anzufassen.

ein kurzer apache-roast weil's sein muss

RIP APACHE (1995–irgendwann) Apache ist nicht schlecht. Apache ist uralt. Es ist wie ein Diesel-Bus aus den 90ern: zuverlässig, überall, aber du willst damit nicht auf die Autobahn. .htaccess-Dateien die bei jedem Request von der Festplatte gelesen werden. Ein Prozess pro Connection. mod_php das direkt im Webserver-Prozess lebt wie ein Parasit. Apache ist das warum "meine Website ist langsam" zwanzig Jahre lang die Standardantwort im Hosting-Business war.

nginx auf ubuntu installieren – für vollständigkeit

⚠ NUR FÜR UBUNTU: Diese Anleitung gilt ausschliesslich für Ubuntu. Wer ein anderes OS benutzt hat die Kontrolle über sein Leben verloren und soll das selbst googlen.

Du kannst nginx via apt aus den Ubuntu-Repos installieren – aber das ist oft eine veraltete Version die Ubuntu irgendwann eingefroren hat. Wer den aktuellen Stable-Build will, nimmt das offizielle nginx-Repo. Dauert 2 Minuten mehr, lohnt sich.

# 1. Abhängigkeiten
sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring

# 2. Offiziellen nginx Signing Key importieren
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

# 3. Stable-Repo eintragen
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

# 4. Offizielles Repo höher priorisieren als Ubuntu-Repo
echo "Package: *
Pin: origin nginx.org
Pin-Priority: 900" | sudo tee /etc/apt/preferences.d/99nginx

# 5. Installieren
sudo apt update && sudo apt install -y nginx

# Version prüfen – sollte aktueller Stable sein
nginx -v
# → nginx version: nginx/1.26.x

Der Unterschied: Ubuntu 24.04 liefert out-of-the-box nginx 1.24.x. Das offizielle Repo gibt dir 1.26.x Stable mit aktuellen Security-Fixes und HTTP/3-Support. Für einen Techblog der über nginx schreibt sollte man wenigstens die aktuelle Version laufen haben.

# Läuft sofort. Kein Neustart. Kein Wizard. Kein Bullshit.
sudo systemctl status nginx
# → active (running)

# Config testen (immer vor reload!)
sudo nginx -t
# → syntax is ok / test is successful

# Graceful reload (keine downtime)
sudo systemctl reload nginx
✓ nginx läuft auf diesem Server. Auf einem t4g.micro für $4/Monat. Und er langweilt sich dabei. Das ist das geilste daran.