nginx-Konfiguration — Server-Blöcke, Reverse Proxy und TLS
Praxis-Referenz zur nginx.conf — server- und location-Blöcke, Reverse Proxy, TLS, Gzip, Caching, Rate Limiting und Security-Header mit Beispielen.
Die nginx.conf ist das Herzstück jedes nginx-Servers: Über Direktiven und verschachtelte Blöcke legst du fest, wie Anfragen angenommen, weitergeleitet und ausgeliefert werden. Diese Referenz sammelt die wichtigsten Snippets für den Alltag — von server- und location-Blöcken über Reverse Proxy und TLS bis zu Gzip, Caching, Rate Limiting und Security-Headern. Je nach Direktive gehören die Beispiele in den http-, server- oder location-Kontext deiner /etc/nginx/nginx.conf oder in die Site-Dateien unter sites-available/. Prüfe jede Änderung mit nginx -t und übernimm sie mit einem Reload — so legt dich kein Tippfehler lahm. Den Dienst selbst steuerst du über das separate nginx-CLI-Cheat-Sheet.
Config-Struktur & Kontexte
Top-Level-Struktur der nginx.conf. Der http-Block umschließt die gesamte Web-Konfiguration. include bindet die Site-Dateien ein.
# nginx.conf top-level structure
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}Einfacher Server-Block (virtueller Host). Legt fest, welchen Port und welche Domain dieser Block bedient.
server {
listen 80;
server_name example.com www.example.com;
root /var/www/html;
index index.php index.html;
}Location-Blöcke matchen die Request-URIs. Exakt (=), Präfix (/), case-insensitiver Regex (~*) und case-sensitiver Regex (~).
location / {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://localhost:3000/;
}
location ~* \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}nginx wertet Location-Blöcke in dieser Prioritäts-Reihenfolge aus. Exakte Treffer (=) gewinnen immer.
# Location match priority (highest to lowest):
# 1. = exact match
# 2. ^~ prefix match (stops regex search)
# 3. ~ case-sensitive regex
# 4. ~* case-insensitive regex
# 5. / prefix match (longest wins)Virtuelle Hosts (HTTP)
Vollständiger einfacher HTTP-Host mit Document-Root, Index-Dateien und getrennten Log-Dateien.
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
index index.php index.html index.htm;
access_log /var/log/nginx/example-access.log;
error_log /var/log/nginx/example-error.log;
location / {
try_files $uri $uri/ =404;
}
}Catch-all-Standard-Server-Block. Verwirft Requests ohne passenden server_name (return 444 schließt die Verbindung).
server {
listen 80 default_server;
server_name _;
return 444;
}Leitet www auf non-www (oder umgekehrt) per permanenter 301-Weiterleitung um.
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}SSL / TLS (HTTPS)
HTTPS-Server-Block mit Let's-Encrypt-Zertifikat. Nur TLS 1.2 und 1.3, schwache Ciphers deaktiviert.
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
}Leitet allen HTTP-Verkehr auf HTTPS um. Oberhalb des SSL-Server-Blocks platzieren.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}SSL-Performance- und Sicherheitsoptionen. Der Session-Cache reduziert den Handshake-Overhead. OCSP-Stapling beschleunigt die Zertifikatsprüfung.
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;listen 443 ssl http2; — Aktiviert HTTP/2 zusätzlich zu SSL. HTTP/2 setzt HTTPS voraus und verbessert die Performance deutlich. Seit nginx 1.25.1 ist die separate Direktive http2 on; empfohlen.
listen 443 ssl http2;add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; — HSTS-Header: weist Browser an, diese Domain ein Jahr lang ausschließlich über HTTPS aufzurufen.
add_header Strict-Transport-Security "max-age=31536000" always;Reverse Proxy
Reverse Proxy: leitet /api/-Requests an eine Backend-App auf Port 3000 weiter. Reicht Client-IP und Protokoll-Header durch.
location /api/ {
proxy_pass http://localhost: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;
}Erforderliche Header für WebSocket-Proxying. Innerhalb des Proxy-Location-Blocks ergänzen.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";Proxy-Timeout- und Buffering-Einstellungen. Erhöhe die Timeouts für langsame Backend-Anwendungen.
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 4 16k;Aktiviert das Caching von Proxy-Antworten. Gecachte Antworten kommen direkt von der Platte und entlasten das Backend.
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
location / {
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://backend;
}PHP-FPM-Integration
Reicht PHP-Requests per Unix-Socket an PHP-FPM weiter. Passe den Socket-Pfad an deine PHP-Version an.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}Reicht PHP-Requests per TCP auf Port 9000 an PHP-FPM weiter. Nutze TCP, wenn FPM auf einem anderen Host läuft.
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}Front-Controller-Muster: leitet alle Nicht-Datei-Requests an index.php (nötig für Laravel, WordPress usw.).
location / {
try_files $uri $uri/ /index.php?$query_string;
}Cacht FastCGI-Antworten (PHP-FPM), um die PHP-Ausführung bei wiederholten Requests zu sparen.
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=php_cache:10m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
location ~ \.php$ {
fastcgi_cache php_cache;
fastcgi_cache_valid 200 5m;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}Rewrites & Weiterleitungen
return 301 https://example.com$request_uri; — Permanente Weiterleitung auf eine neue URL. Schneller als rewrite: keine Regex-Verarbeitung.
return 301 https://example.com$request_uri;return 302 /maintenance.html; — Temporäre Weiterleitung auf eine Wartungsseite.
return 302 /maintenance.html;Rewrite-Regeln: 'permanent' = 301, 'redirect' = 302. Nutze nach Möglichkeit return; rewrite für Muster-Abgleich.
rewrite ^/old-page$ /new-page permanent;
rewrite ^/blog/(.*)$ /posts/$1 redirect;rewrite ^/index\.php/?(.*)$ /$1 permanent; — Entfernt index.php per permanenter Weiterleitung aus allen URLs.
rewrite ^/index\.php/?(.*)$ /$1 permanent;Entfernt www von jeder Domain über eine Regex-Capture-Gruppe. Besser einen separaten Server-Block dafür verwenden.
if ($host ~* ^www\.(.+)$) {
return 301 https://$1$request_uri;
}Eigene Fehlerseiten. Der Location-Block liefert die Fehlerseite aus einem bestimmten Pfad aus.
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/html;
}Gzip-Komprimierung
Aktiviert Gzip-Komprimierung für gängige textbasierte Content-Types. Innerhalb des http-Blocks platzieren.
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;gzip_disable "msie6"; — Deaktiviert Gzip für Internet Explorer 6 (fehlerhafte Gzip-Implementierung). Bedenkenlos einsetzbar.
gzip_disable "msie6";gunzip on; — Dekomprimiert Gzip-Antworten vom Upstream, bevor sie an Clients ohne Gzip-Unterstützung gehen.
gunzip on;Statische Dateien & Browser-Caching
Cacht Bilder 30 Tage im Browser. 'immutable' sagt dem Browser, dass er nicht revalidieren muss.
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}Cacht CSS und JS ein Jahr lang. Funktioniert am besten mit versionierten/gehashten Dateinamen.
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}Langzeit-Caching für Web-Fonts. Der CORS-Header ist nötig, wenn Fonts cross-origin geladen werden.
location ~* \.(woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin "*";
}Unterdrückt 404-Log-Einträge für favicon.ico und robots.txt (sehr häufige Requests).
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}Performance-Grundlagen für den http-Block. sendfile ermöglicht Dateiübertragung auf Kernel-Ebene. keepalive reduziert den Verbindungs-Overhead.
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;Security-Header
Essenzielle Security-Header. 'always' stellt sicher, dass der Header auch bei Fehlerantworten gesetzt wird.
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always; — Content-Security-Policy-Header. Schränkt ein, welche Ressourcen der Browser laden darf. Passe die Quellen an deine App an.
add_header Content-Security-Policy "default-src 'self';" always;add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; — Permissions-Policy: deaktiviert Kamera-, Mikrofon- und Geolocation-APIs für diese Seite.
add_header Permissions-Policy "camera=(), microphone=()" always;server_tokens off; — Verbirgt die nginx-Versionsnummer in HTTP-Response-Headern und auf Fehlerseiten.
server_tokens off;Sperrt den Zugriff auf versteckte Dateien und Verzeichnisse (z. B. .git, .env, .htaccess). Lässt .well-known für ACME-Challenges zu.
location ~ /\.(?!well-known) {
deny all;
}Sperrt den direkten Zugriff auf sensible Dateitypen (Backups, Configs, Logs usw.).
location ~* \.(bak|conf|dist|fla|inc|ini|log|psd|sh|sql|swp)$ {
deny all;
}Rate Limiting & Zugriffskontrolle
Rate Limiting: erlaubt max. 10 Requests/Sekunde pro IP, mit Burst bis 20. Überzählige Requests erhalten 503.
# In http block:
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# In server or location block:
limit_req zone=req_limit burst=20 nodelay;Begrenzt die Zahl gleichzeitiger Verbindungen pro IP auf 10.
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
limit_conn conn_limit 10;Erlaubt bestimmte IPs oder Subnetze und sperrt alle anderen. Praktisch für Admin-Bereiche.
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;HTTP-Basic-Authentifizierung. .htpasswd erzeugen mit: htpasswd -c /etc/nginx/.htpasswd username
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;client_max_body_size 50M; — Setzt die maximal erlaubte Größe des Client-Request-Bodys. Für Datei-Uploads erhöhen.
client_max_body_size 50M;Load Balancing
Round-Robin-Load-Balancing über drei Backend-Server. Requests werden gleichmäßig verteilt.
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
location / {
proxy_pass http://backend;
}
}Least-Connections-Load-Balancing: schickt neue Requests an den Server mit den wenigsten aktiven Verbindungen.
upstream backend {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}IP-Hash-Load-Balancing: leitet dieselbe Client-IP stets an dasselbe Backend (Session-Persistenz).
upstream backend {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}Gewichtetes Load-Balancing mit Backup-Server. Der Backup erhält nur Requests, wenn die primären Server ausgefallen sind.
upstream backend {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=1;
server 192.168.1.12:8080 backup;
}Passive Health-Checks: markiert einen Server nach 3 Fehlversuchen als ausgefallen und überspringt ihn dann 30 Sekunden.
upstream backend {
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}Logging
Definiert ein eigenes Log-Format namens 'main' im http-Block. Das ist das Combined-Log-Format.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';Aktiviert Access-Logging im 'main'-Format und Error-Logging auf Level warn.
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;access_log off; — Deaktiviert Access-Logging für einen Server- oder Location-Block (praktisch für statische Assets).
access_log off;error_log /var/log/nginx/error.log debug; — Setzt das Error-Log-Level auf debug für ausführliche Ausgabe. Level: debug, info, notice, warn, error, crit, alert, emerg.
error_log /var/log/nginx/error.log debug;Access-Log im JSON-Format. Praktisch für Log-Aggregations-Tools (ELK-Stack, Loki usw.).
log_format json_combined escape=json
'{"time": "$time_local", "remote_addr": "$remote_addr", '
'"request": "$request", "status": $status, '
'"bytes": $body_bytes_sent, "ua": "$http_user_agent"}';Variablen-Referenz
$host — Der Wert des Host-Headers der Anfrage (oder server_name, falls kein Host-Header vorhanden ist).
proxy_set_header Host $host;$request_uri — Die vollständige originale Request-URI inklusive Query-String, wie vom Client gesendet.
return 301 https://example.com$request_uri;$uri — Die aktuelle (ggf. umgeschriebene) Request-URI ohne Query-String.
try_files $uri $uri/ /index.php;$args / $query_string — Der Query-String-Teil der Request-URI (beide Variablen sind gleichwertig).
proxy_pass http://backend?$args;$remote_addr — Die IP-Adresse des sich verbindenden Clients.
add_header X-Client-IP $remote_addr;$scheme — Das Schema der Anfrage: 'http' oder 'https'.
proxy_set_header X-Forwarded-Proto $scheme;$server_name — Der server_name des Server-Blocks, der die Anfrage gematcht hat.
add_header X-Served-By $server_name;$http_<header> — Zugriff auf jeden Request-Header. Bindestriche werden zu Unterstrichen: $http_user_agent, $http_x_forwarded_for.
if ($http_user_agent ~* 'bot') { return 403; } Fazit
Eine durchdachte nginx-Konfiguration entscheidet über Performance, Sicherheit und Stabilität deiner Seite. Beginne mit einem sauberen Server-Block, ergänze TLS und Security-Header und aktiviere Gzip sowie Browser-Caching — und schütze öffentliche Endpunkte mit Rate Limiting gegen Missbrauch. Teste jede Änderung mit nginx -t, bevor du neu lädst, und halte deine TLS-Einstellungen mit Bedacht aktuell.
Weiterführende Links
- nginx — offizielle Dokumentation — vollständige Direktiven-Referenz (englisch)
- nginx Beginner's Guide — Einstieg in Konfiguration und Konzepte (englisch)
- Nginx — Wikipedia — Hintergrund und Geschichte