
Domain & DNS richtig einrichten 2026 – A, CNAME, MX erklärt
Domain & DNS richtig einrichten 2026: A-, CNAME-, MX- und TXT-Records erklärt, inklusive TTL-Werten, dig-Befehlen, DNSSEC und Migrations-Checkliste.
Der FastCGI Cache ist ein in Nginx integrierter Vollseiten-Cache, der die komplette HTML-Ausgabe von PHP-FPM-Anwendungen auf der Festplatte zwischenspeichert. Statt bei jedem Request WordPress, Joomla oder Laravel komplett zu booten – Datenbankabfragen, Plugin-Loading, Template-Rendering – liefert Nginx die fertige HTML-Seite direkt aus dem Cache aus. In der Praxis bedeutet das: statt 250–800 ms Antwortzeit sind 5–20 ms möglich, und die CPU-Last sinkt um 70–90 %.
Der entscheidende Vorteil gegenüber Redis Object Cache oder APCu: Der FastCGI Cache greift vor PHP. Es wird kein PHP-FPM-Worker belegt, keine MySQL-Verbindung geöffnet, kein Autoloader gestartet. Bei einem typischen WordPress mit 15 aktiven Plugins sparen Sie pro gecachtem Request rund 60–120 MB Speicher-Allokation und 40–90 ms CPU-Zeit.
Lohnenswert ist der FastCGI Cache ab etwa 5.000 Seitenaufrufen pro Tag oder wenn Sie mit einem kleinen VPS (1 vCPU, 2 GB RAM, ca. 5–8 €/Monat) Traffic-Spitzen abfangen wollen. Für rein statische Sites ist er überflüssig, für Shops mit Warenkorb-Logik nur mit sorgfältigem Bypass-Setup.
Ein Nachteil: Der Cache liegt auf der Platte, nicht im RAM. Bei NVMe-SSDs (bei Hetzner, Netcup und Contabo seit 2024 Standard) liegt die Latenz bei 0,1–0,3 ms pro Lookup – vernachlässigbar. Bei alten SATA-SSDs oder gar HDDs würde ich stattdessen auf Varnish oder Redis setzen.
Sie brauchen Nginx 1.24 oder neuer (empfohlen: 1.26/1.28 stable), PHP-FPM 8.2+ und ausreichend Plattenplatz. Der Cache-Ordner sollte auf einer eigenen Partition oder mindestens einem eigenen Verzeichnis mit noatime-Mount-Option liegen, damit das Lesen keine Schreibzugriffe triggert.
Als Richtwert für die Cache-Größe gilt: 1 GB Cache pro 10.000 gecachte Seiten bei durchschnittlich 100 KB HTML pro Seite. Bei einem Magazin mit 50.000 Artikeln planen Sie 5–8 GB ein. Prüfen Sie freien Platz mit df -h /var/cache/nginx.
Die RAM-Anforderung hängt von keys_zone ab: Pro 1 MB Shared Memory lassen sich etwa 8.000 Cache-Keys verwalten. Für 100.000 Seiten reichen also 16 MB. Das ist der häufigste Anfängerfehler – keys_zone wird mit der Cache-Größe verwechselt.
ngx_http_fastcgi_module (Standard in allen Distro-Paketen)Installation auf Debian 13 / Ubuntu 24.04:
apt update
apt install nginx-full php8.3-fpm -y
nginx -V 2>&1 | grep -o 'with-http_fastcgi_module'
# Ausgabe: with-http_fastcgi_module
mkdir -p /var/cache/nginx/fastcgi
chown -R www-data:www-data /var/cache/nginx
Die globale Cache-Definition gehört in den http-Block, typischerweise in /etc/nginx/nginx.conf oder eine separate Datei unter /etc/nginx/conf.d/cache.conf. Wichtig: use_temp_path=off vermeidet einen unnötigen Kopiervorgang zwischen zwei Verzeichnissen und spart bei hoher Last bis zu 15 % I/O.
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=FASTCGICACHE:100m
max_size=10g
inactive=60m
use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header updating
http_500 http_503;
fastcgi_cache_background_update on;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 5s;
fastcgi_cache_min_uses 2;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_valid 404 1m;
levels=1:2 erzeugt eine zweistufige Verzeichnishierarchie (z. B. /a/3b/…). Das verhindert, dass ein einzelnes Verzeichnis mit Hunderttausenden Dateien zum Flaschenhals wird – auf ext4 ein echtes Problem, auf XFS weniger. inactive=60m bedeutet: Seiten, die 60 Minuten nicht abgerufen wurden, werden beim nächsten Platzmangel zuerst gelöscht.
fastcgi_cache_lock on ist der wichtigste Parameter für Stabilität: Wenn 50 gleichzeitige Requests eine noch nicht gecachte Seite anfordern, rendert nur ein Request PHP, die anderen 49 warten auf das Ergebnis. Ohne Lock entsteht bei Traffic-Spikes ein „Cache Stampede", der Ihre PHP-FPM-Worker in Sekunden ausschöpft.
Nach der Änderung immer testen und neu laden:
nginx -t
systemctl reload nginx
Der Cache-Key bestimmt, welche Requests als identisch gelten. Ein falscher Key ist die häufigste Ursache für „Warum sehe ich die Seite von jemand anderem?"-Tickets. Die Standardformel $scheme$request_method$host$request_uri ist für die meisten Setups korrekt, hat aber drei Fallstricke.
Erstens: $request_uri enthält Query-Strings. Damit wird /seite/?utm_source=newsletter separat gecacht von /seite/. Bei Marketing-Kampagnen mit vielen UTM-Parametern explodiert der Cache. Lösung: In WordPress den utm_*-Parameter vorher strippen oder $uri statt $request_uri verwenden, wenn Sie keine Parameter-abhängigen Seiten haben.
Zweitens: Mobile und Desktop. Wenn Sie kein separates Mobile-Theme ausliefern, ignorieren Sie das. Andernfalls ergänzen Sie $http_user_agent – aber vorsichtig, das fragmentiert den Cache stark.
Drittens: Multi-Domain-Setups. $host im Key sorgt dafür, dass shop.example.com und www.example.com getrennte Cache-Einträge bekommen. Das ist gewünscht, kostet aber doppelten Speicher.
| Cache-Key-Variante | Einsatz | Cache-Größe |
|---|---|---|
$scheme$request_method$host$request_uri | Standard, WordPress ohne UTM | 1x |
$scheme$request_method$host$uri | Parameter ignorieren | 0,3x |
+ $http_user_agent | Separates Mobile-Theme | 2–4x |
+ $geoip_country_code | Länderspezifische Inhalte | 5–20x |
Faustregel: Jede Variable im Cache-Key multipliziert die Anzahl der Cache-Einträge. Halten Sie den Key so kurz wie möglich und so lang wie nötig.
In der Server-Block-Datei (/etc/nginx/sites-available/example.com) definieren Sie, welche Requests überhaupt gecacht werden dürfen. Beginnen Sie mit einer Variable $skip_cache, die per map oder if gesetzt wird.
server {
listen 443 ssl http2;
server_name example.com;
root /var/www/example.com;
index index.php;
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-login.php|/cart/|/checkout/|/my-account/") {
set $skip_cache 1;
}
if ($http_cookie ~* "wordpress_logged_in|comment_author|woocommerce_items_in_cart|wp_woocommerce_session") {
set $skip_cache 1;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_cache FASTCGICACHE;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_valid 404 1m;
add_header X-FastCGI-Cache $upstream_cache_status always;
}
}
Der Unterschied zwischen fastcgi_cache_bypass und fastcgi_no_cache ist wichtig: Ersteres liest den Cache nicht (liefert also immer frisch), Letzteres schreibt das Ergebnis nicht in den Cache. Für $skip_cache brauchen Sie beides – sonst landet eine eingeloggte Admin-Ansicht im Cache und wird Gästen ausgeliefert.
Für WooCommerce würde ich zusätzlich /warenkorb/, /kasse/ und alle URLs mit ?add-to-cart= ausschließen. Produktkategorien und einzelne Produktseiten lassen sich dagegen problemlos cachen – sie sind für alle Besucher identisch, solange der Warenkorb-Status über Cookies läuft.
Bei Laravel oder Symfony heißt der Cache-Key oft anders, weil die App über index.php mit Query-String routet. Dort funktioniert $uri nicht – verwenden Sie $request_uri und schließen Sie /api/ und /admin/ explizit aus.
Der gefährlichste Fehler beim FastCGI Cache ist das Ausliefern personalisierter Inhalte an falsche Nutzer. Nginx prüft standardmäßig Set-Cookie im Response-Header und cacht solche Antworten nicht – das ist ein Sicherheitsnetz, aber kein Ersatz für explizite Regeln.
Wenn Ihre Anwendung Set-Cookie bei jedem Request sendet (viele WordPress-Setups mit Cookie-Consent-Plugins tun das), wird der Cache nie gefüllt. In diesem Fall müssen Sie fastcgi_ignore_headers Set-Cookie setzen – aber nur, wenn Sie sicher sind, dass das Cookie keine personalisierten Inhalte steuert. Prüfen Sie das vorher mit:
curl -sI https://example.com/ | grep -i set-cookie
Ein zweites Sicherheitsnetz ist fastcgi_ignore_headers Cache-Control Expires Vary. Damit überschreiben Sie die Cache-Header der Anwendung. Sinnvoll, wenn WordPress per Plugin Cache-Control: no-cache, must-revalidate sendet und Sie das bewusst aushebeln wollen.
Für DSGVO-Konformität gilt: Der Cache darf keine personenbezogenen Daten enthalten. Kontrollieren Sie regelmäßig mit einem Grep über den Cache-Ordner, ob E-Mail-Adressen oder Session-IDs im HTML landen:
grep -rl "@" /var/cache/nginx/fastcgi | head -20
Wenn Sie Treffer sehen, ist Ihr Bypass-Setup lückenhaft. Typische Ursachen sind fehlende Cookie-Ausschlüsse oder Kommentarformulare, deren Bestätigungsseite gecacht wird.
Ein Cache ohne Invalidierung ist ein Bug-Generator: Sie veröffentlichen einen Artikel und Besucher sehen 60 Minuten lang die alte Version. Es gibt drei Wege, das zu lösen.
Weg 1 – Zeitbasiert (einfach): fastcgi_cache_valid 200 10m. Nach 10 Minuten wird neu gerendert. Für Blogs mit seltenen Updates völlig ausreichend, aber bei News-Seiten zu träge.
Weg 2 – Purge-Endpoint (empfohlen): Das Modul ngx_cache_purge (Paket libnginx-mod-http-cache-purge) erlaubt gezieltes Löschen einzelner URLs.
apt install libnginx-mod-http-cache-purge -y
# im server-Block:
location ~ /purge(/.*) {
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
fastcgi_cache_purge FASTCGICACHE "$scheme$request_method$host$1";
}
Nach jedem WordPress-Post-Publish feuert Ihr Theme oder ein Snippet einen Request:
curl -s "http://127.0.0.1/purge/permalalink-slug/"
Weg 3 – Kompletter Flush: Bei größeren Änderungen (Theme-Update, Plugin-Aktivierung) löschen Sie alles:
rm -rf /var/cache/nginx/fastcgi/*
systemctl reload nginx
Wichtig: Löschen Sie das Verzeichnis nicht selbst, nur den Inhalt. Nginx legt die Struktur beim nächsten Request neu an, aber ein fehlendes Root-Verzeichnis führt zu Permission denied-Fehlern im Log. Binden Sie den Flush in Ihre Deployment-Pipeline ein – nach jedem git pull und composer install sollte ein Cache-Flush folgen.
Die Cache-Hit-Rate ist Ihre wichtigste Kennzahl. Alles unter 70 % deutet auf ein Bypass-Problem hin. Nginx liefert den Status über die Variable $upstream_cache_status mit den Werten HIT, MISS, BYPASS, EXPIRED, STALE, UPDATING und REVALIDATED.
Definieren Sie ein eigenes Log-Format im http-Block:
log_format cache '$remote_addr - $upstream_cache_status [$time_local] '
'"$request" $status $body_bytes_sent $request_time';
access_log /var/log/nginx/cache.log cache;
Auswertung der Hit-Rate (Feld 3 ist der Cache-Status):
awk '{print $3}' /var/log/nginx/cache.log | sort | uniq -c | sort -rn
# Beispielausgabe:
# 84213 HIT
# 11204 MISS
# 3187 BYPASS
# 412 EXPIRED
In diesem Beispiel liegt die Hit-Rate bei 84,5 % – ein guter Wert. Ein hoher BYPASS-Anteil ist normal, wenn Sie viel Admin-Traffic haben. Ein hoher EXPIRED-Anteil bedeutet, dass Ihre TTL zu kurz ist.
Benchmark mit wrk auf einem 2-vCPU-VPS (Hetzner CX22, PHP 8.3, WordPress 6.8, 22 Plugins):
| Szenario | Requests/s | Latenz p99 | CPU-Last |
|---|---|---|---|
| Ohne Cache | 178 | 1.240 ms | 98 % |
| Redis Object Cache | 640 | 310 ms | 74 % |
| FastCGI Cache (MISS) | 195 | 1.180 ms | 96 % |
| FastCGI Cache (HIT) | 4.850 | 34 ms | 11 % |
Der Sprung von 178 auf 4.850 Requests/s entspricht Faktor 27. Wichtig: Diese Werte gelten für gecachte Seiten. Der erste Request nach Ablauf der TTL ist immer langsam – deshalb ist fastcgi_cache_background_update on so wertvoll: Ein Besucher bekommt die alte (STALE) Version, während im Hintergrund neu gerendert wird.
Cache füllt sich nicht: Prüfen Sie mit curl -I https://example.com/, ob X-FastCGI-Cache: MISS und beim zweiten Aufruf HIT erscheint. Bleibt es bei MISS, sendet PHP einen Set-Cookie-Header oder Cache-Control: no-cache. Log-Check: tail -f /var/log/nginx/error.log.
502 Bad Gateway nach Cache-Aktivierung: Meist ein Rechte-Problem. Nginx läuft als www-data, der Cache-Ordner gehört aber root. Lösung: chown -R www-data:www-data /var/cache/nginx und SELinux-Kontext auf RHEL-basierten Systemen mit restorecon -Rv /var/cache/nginx setzen.
Alte Inhalte nach Update: TTL zu lang oder Purge-Endpoint nicht erreichbar. Testen Sie curl -v "http://127.0.0.1/purge/testseite/" – bei Erfolg antwortet Nginx mit 200 Successful purge.
Cache wächst unkontrolliert: max_size vergessen oder inactive zu hoch. Nginx räumt den Cache nur beim Schreiben neuer Einträge auf – ein voller Cache wird also nie von selbst geleert, wenn kein Traffic kommt.
nginx: [emerg] unknown directive "fastcgi_cache_path" → Modul fehlt, nginx-full statt nginx-light installierenfastcgi_buffers zu klein, auf 16 16k erhöheniowait-Werte → use_temp_path=off fehlt$host im Key prüfenDer Cache-Ordner darf niemals über HTTP erreichbar sein. Prüfen Sie, dass /var/cache/nginx außerhalb des root-Verzeichnisses liegt und keine location darauf zeigt. Ein versehentlich ausgeliefertes Cache-Verzeichnis gibt Angreifern Einblick in alle gecachten Seiten inklusive eventueller Fehlermeldungen.
Aus DSGVO-Sicht gilt: Gecachte Seiten sind Kopien Ihrer Inhalte. Wenn Sie Formulare, Session-Daten oder personalisierte Empfehlungen ausliefern, müssen diese zwingend vom Cache ausgeschlossen sein. Dokumentieren Sie Ihr Bypass-Setup im Verarbeitungsverzeichnis – das ist bei Audits ein häufiger Prüfpunkt.
Setzen Sie außerdem fastcgi_hide_header X-Powered-By und fastcgi_hide_header X-Pingback, um Versionsinformationen nicht in gecachten Antworten zu verteilen. Bei WordPress kommt oft noch Link: <https://example.com/wp-json/>; rel="https://api.w.org/" dazu – harmlos, aber für Angreifer ein Hinweis auf REST-API-Endpunkte.
Ein letzter Punkt: Cache-Poisoning. Wenn Ihre Anwendung Header wie X-Forwarded-Host ungefiltert in die Ausgabe schreibt und diese in den Cache gelangen, können Angreifer manipulierte Inhalte für alle Besucher platzieren. Prüfen Sie nach jedem Deployment mit curl -H "X-Forwarded-Host: evil.com" https://example.com/, ob Ihre Domain in der Antwort auftaucht.
Alle drei lösen unterschiedliche Probleme. Der FastCGI Cache ist ein Vollseiten-Cache auf Dateiebene, Redis Object Cache speichert einzelne Datenbankabfragen und Objekte im RAM, Varnish ist ein vorgeschalteter HTTP-Beschleuniger mit eigener Konfigurationssprache (VCL).
| Kriterium | FastCGI Cache | Redis Object Cache | Varnish |
|---|---|---|---|
| Cache-Ebene | Vor PHP, ganze Seite | In PHP, Objekte/Queries | Vor Nginx, ganze Seite |
| Speicherort | NVMe-SSD | RAM | RAM |
| Speedup (HIT) | 20–30x | 3–5x | 25–40x |
| Setup-Aufwand | Niedrig (30 Min.) | Niedrig (15 Min.) | Hoch (2–4 Std.) |
| RAM-Bedarf | ~100 MB | 256 MB–2 GB | 1–4 GB |
| SSL-Terminierung | Ja (nativ) | N/A | Nein (Nginx davor) |
| Purging | URL-genau | Key-genau | Tag-basiert (BAN) |
Die beste Kombination für WordPress 2026 ist FastCGI Cache plus Redis Object Cache: Der FastCGI Cache liefert die fertige Seite für Gäste, Redis beschleunigt die Requests, die den Cache umgehen (eingeloggte Nutzer, Warenkorb, Admin). Der zusätzliche RAM-Bedarf von 256 MB kostet bei Hetzner rund 1,50 €/Monat – bei einem Shop mit 50.000 Besuchern pro Monat ist das die günstigste Performance-Investition überhaupt.
Varnish lohnt sich erst ab etwa 1 Mio. Requests pro Tag oder wenn Sie Edge-Side-Includes, komplexe Header-Manipulation oder ESI-Fragmente benötigen. Für 95 % aller WordPress-, Shopware- und Laravel-Projekte ist der FastCGI Cache die pragmatischere Wahl: weniger bewegliche Teile, native TLS-Unterstützung, keine separate Konfigurationssprache.
Für maximale Performance kombinieren Sie den Cache mit HTTP/2, Brotli-Kompression (ngx_brotli) und einem open_file_cache max=10000 inactive=30s. Letzteres spart bei statischen Assets weitere 5–10 % Systemzeit.
Der Shared-Memory-Bereich (keys_zone) benötigt rund 1 MB pro 8.000 gecachte URLs. Für 100.000 Seiten reichen 16 MB, für die meisten Sites sind 100 MB großzügig dimensioniert. Der eigentliche HTML-Inhalt liegt auf der SSD und belegt dort je nach max_size zwischen 1 und 20 GB. RAM und Cache-Größe sind also zwei völlig getrennte Werte.
In 90 % der Fälle sendet PHP einen Set-Cookie-Header, den Nginx als Personalisierung interpretiert. Prüfen Sie mit curl -sI https://example.com/ | grep -i set-cookie. Ist ein Cookie dabei, müssen Sie entweder das verursachende Plugin deaktivieren oder bewusst fastcgi_ignore_headers Set-Cookie setzen – Letzteres nur, wenn das Cookie keine nutzerspezifischen Inhalte steuert.
Ja, mit Einschränkungen. Produktseiten, Kategorien und statische Seiten lassen sich problemlos cachen. Warenkorb, Kasse und Mein-Konto-Bereich müssen per Cookie-Ausschluss (woocommerce_items_in_cart, wp_woocommerce_session) vom Cache ausgenommen werden. Bei Shops mit dynamischen Preisen (B2B, Kundengruppen) würde ich auf Object Cache plus OPcache setzen und den Vollseiten-Cache nur für Gäste aktivieren.
Für Blogs und Unternehmensseiten sind 60 Minuten ein guter Startwert, für News-Portale 5–10 Minuten, für Shops 15–30 Minuten. Nutzen Sie zusätzlich fastcgi_cache_background_update on, damit Besucher auch nach Ablauf der TTL sofort die alte Version bekommen, während im Hintergrund neu gerendert wird. Bei aktivem Purge-Endpoint können Sie die TTL auf 24 Stunden setzen und nur bei Änderungen gezielt löschen.
Ja, der Cache arbeitet protokollunabhängig unterhalb der HTTP-Schicht. Nginx unterstützt HTTP/3 seit Version 1.25 über das ngx_http_v3_module. Achten Sie darauf, dass listen 443 quic reuseport; und der Alt-Svc-Header korrekt gesetzt sind – der Cache-Key bleibt unverändert, da $scheme weiterhin https liefert.