Skip to content

Nginx-Lasttests und Angriffsschutz(DDOS-Schutz): Praxisbeispiel mit k6

Last updated on 14. August 2025

Kontext: Nginx + PHP-FPM (WordPress), vorgeschaltetes CDN (Cloudflare), Lasttests mit k6; Ziel: Stabilität unter Last, Schutz gegen Cache-Bypass und Bot-Traffic.

Einleitung

Webserver müssen nicht nur normalen Benutzerverkehr verarbeiten, sondern auch böswillige Anfragen, Bot-Angriffe und Lastspitzen abwehren können. Besonders bei Nginx + PHP-FPM kann eine bestimmte Art von Requests die CPU stark belasten und im schlimmsten Fall zum Dienstabbruch führen. In diesem Leitfaden zeigen wir Schritt für Schritt, wie wir mit k6 realistische Last erzeugt und Nginx so gehärtet haben, dass der Server auch bei Cache-Bypass-Wellen und hoher Parallelität stabil bleibt.

Was ist k6?

k6 ist ein modernes, in Go entwickeltes Open-Source-Lasttest-Tool mit JavaScript-Skripting. Es eignet sich für realistische Szenarien (Ramp-up, konstante Raten) und liefert präzise Metriken (Latenz, Fehlerrate, Durchsatz).

  • Testskripte in JavaScript
  • Stufenweise Erhöhung/Verringerung der Last
  • HTTP, WebSocket und API-Tests
  • Echtzeit-Reporting der Ergebnisse

Installation (Debian/Ubuntu)

sudo apt update
sudo apt install gnupg2 ca-certificates
curl -fsSL https://dl.k6.io/key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/k6-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt update
sudo apt install k6

Test-Szenario (miss_test2.js)

Ziel des Szenarios: in kurzer Zeit viele Anfragen generieren, u. a. mit Cache-Bypass-Parametern, um CPU-Last und Fehlerraten realistisch zu beobachten.

import http from 'k6/http';
import { sleep } from 'k6';

export let options = {
  stages: [
    { duration: '30s', target: 150 },
    { duration: '30s', target: 150 },
    { duration: '35s', target: 400 },
  ],
  thresholds: {
    http_req_duration: ['p(95)<800'],
    http_req_failed: ['rate<0.05'],
  },
};

export default function () {
  const target = __ENV.TARGET || 'https://ihre-seite.de';
  let res = http.get(`${target}/?_k6=${Date.now()}`, {
    headers: { 'Cache-Control': 'no-cache', 'User-Agent': 'k6-loadtest' }
  });
  sleep(1);
}
k6 run -e TARGET=https://ihre-seite.de miss_test2.js

Ausgangssituation

  • Schneller Anstieg des CPU-Loads (z. B. von 0.50 → 1.19)
  • Viele 503 Service Unavailable-Antworten
  • Hohe Fehlerrate bei gleichzeitigen Requests
  • nocache-Parameter leitete alles direkt zu PHP-FPM → hohe CPU-Last

Nginx-Konfigurationsänderungen

1) nginx.conf – Grundkonfiguration

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 8192;
    multi_accept on;
}

http {
    # Real client IP hinter Cloudflare
    include /etc/nginx/conf.d/realip.conf;
    real_ip_header CF-Connecting-IP;
    real_ip_recursive on;

    # IP-Anonymisierung für Logs
    map $remote_addr $ip_anonym1 {
        default 0.0.0;
        "~(?P<ip>(\d+)\.(\d+)\.(\d+))\.\d+" $ip;
        "~(?P<ip>[^:]+:[^:]+):" $ip;
    }
    map $remote_addr $ip_anonym2 {
        default .0;
        "~(?P<ip>(\d+)\.(\d+)\.(\d+))\.\d+" .0;
        "~(?P<ip>[^:]+:[^:]+):" ::;
    }
    map $ip_anonym1$ip_anonym2 $ip_anonymized {
        default 0.0.0.0;
        "~(?P<ip>.*)" $ip;
    }

    log_format anonymized '$ip_anonymized - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent"';

    # Core settings
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    server_tokens off;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Timeouts / keepalive
    keepalive_timeout 5;
    keepalive_requests 1000;
    client_header_timeout 10s;
    client_body_timeout   10s;
    send_timeout          15s;
    reset_timedout_connection on;

    # SSL
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    # FastCGI (PHP-FPM) Puffer & Timeouts
    fastcgi_connect_timeout 5s;
    fastcgi_send_timeout    60s;
    fastcgi_read_timeout    60s;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;

    # Gzip
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 256;
    gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;

    # Anonymized access log
    access_log /var/log/nginx/access.log anonymized;

    # Include site configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

2) /etc/nginx/conf.d/limit.conf – Limit-Zonen

limit_req_zone  $binary_remote_addr zone=reqzone:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=connzone:10m;

3) /etc/nginx/conf.d/realip.conf – Cloudflare Real-IP

Damit Nginx die echte Besucher-IP statt Cloudflare-IPs sieht, werden die offiziellen Cloudflare-Netze als vertrauenswürdig hinterlegt und der Header CF-Connecting-IP ausgewertet:

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;

Ergebnis: Logs und Schutzmechanismen (z. B. limit_req/limit_conn) beziehen sich auf die echte Besucher-IP, nicht auf Cloudflare-IPs.

Da sich die IP-Bereiche von Cloudflare gelegentlich ändern, ist es empfehlenswert, diese Liste automatisch aktuell zu halten. Eine Schritt-für-Schritt-Anleitung dafür findest du hier: Nginx: Cloudflare Real-IP-Liste automatisch aktualisieren (Cron)

4)/etc/nginx/conf.d/fastcgi_cache.conf – fastcgi_cache

fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=FPMCACHE:100m inactive=60m max_size=2g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

5) vHost – Limits, Parameter-Blockade & PHP-Micro-Cache

# innerhalb des server { }-Blocks

# IP-basierte Limits
limit_conn connzone 30;
limit_req  zone=reqzone burst=30 nodelay;

# Cache-Bypass-Parameter blockieren (optional, aber sinnvoll)
if ($arg_nocache) { return 403; }
if ($arg__bust)   { return 403; }

# PHP-Location mit FastCGI-Micro-Cache (anonyme Nutzer)
# location @php { ... } oder location ~ \.php$ { ... }
set $no_cache 0;
if ($request_method = POST) { set $no_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_(?!test_cookie)|wp-postpass|wordpress_logged_in") { set $no_cache 1; }

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache    $no_cache;

fastcgi_cache       FPMCACHE;
fastcgi_cache_valid 200 301 302 1m;
add_header X-Cache $upstream_cache_status;

Ergebnisse nach den Anpassungen

  • CPU-Load stabil bei ~1.0–1.2 (unter kontrollierter Last)
  • Schädliche Requests → 403 Forbidden bzw. werden durch Limits gedrosselt
  • Normale Besucher → keine spürbaren Einbußen
  • Deutlich weniger 503-Antworten

Fazit

Mit k6 als Lasttest-Werkzeug und gezielten Nginx-Anpassungen (Real-IP, limit_req/limit_conn, Micro-Cache) bleibt ein PHP-basierter Webserver auch bei Cache-Bypass-Wellen und hoher Parallelität stabil. Die Maßnahmen reduzieren die CPU-Last, verhindern unnötige PHP-Ausführung und schützen die Verfügbarkeit ohne den normalen Benutzerverkehr zu beeinträchtigen.

Tipp: Passe rate/burst an dein Traffic-Profil an, überprüfe regelmäßig die Cloudflare-IP-Liste und nutze das X-Cache-Header-Monitoring für schnelle Checks (MISS → HIT).

Download: Beispielkonfiguration für Anti-DDoS mit Nginx

Alle in diesem Beitrag erwähnten Konfigurationsdateien können Sie unter folgendem Link als ZIP-Archiv herunterladen. Die Dateien sind gemäß der /etc/nginx/-Verzeichnisstruktur organisiert. Bitte passen Sie die Inhalte vor der Verwendung an Ihre eigenen Anforderungen an.

SHA256-Hash:

530bb4b46ce1c8b299f34db8dc9b1cdaba18e7d5ec3e7a8c9f6d58d6e620d62f

Archivinhalt:

  • /etc/nginx/nginx.conf
  • /etc/nginx/conf.d/fastcgi_cache.conf
  • /etc/nginx/conf.d/limit.conf
  • /etc/nginx/conf.d/realip.conf
  • /etc/nginx/sites-available/example.com.vhost

Hinweis:: Wenn dir dieser Beitrag gefallen oder geholfen hat, kannst du mich gerne mit einer kleinen Unterstützung motivieren 😊

☕ Buy me a coffee

💙 Support via PayPal

₿/Ξ: Donate with Bitcoin
Address: bc1qt7wc6jfth4t2szc2hp6340sqp3y0pa9r3ywgrr

Show QR codeCrypto QR code
Published inLinux & SerververwaltungNginx & Hosting Know-HowPHP & MySQL Notizen

Schreib den ersten Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert