1. Überblick – WordPress‑Stack imatrix.at
Komponenten:
- Nginx 1.22.1 (OpenSSL 3.0.x, HTTP/2, hardened Build)
- PHP‑FPM 8.2 (Unix‑Socket)
- Redis (lokal, protected‑mode)
- FastCGI‑Cache (Nginx)
- Hardening‑Layer (Nginx‑Snippets)
- Security‑Header & Rate‑Limits
- Certbot‑TLS
Architektur:
Client → Nginx (TLS, Hardening, FastCGI‑Cache) → PHP‑FPM → WordPress → Redis (Object Cache) → zurück über Nginx zum Client.

2. Nginx Hauptkonfiguration (`/etc/nginx/nginx.conf`)
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 64M;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" rt=$request_time '
'cache=$upstream_cache_status';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/conf.d/security.conf;
include /etc/nginx/conf.d/cache.conf;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml \
application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 1024;
include /etc/nginx/sites-enabled/*;
server {
location ~* \.(?:sql|log|sh|bak|conf)$ { deny all; }
location ~* /(?:\.git|\.env|composer\.json|composer\.lock) { deny all; }
}
}
3. Sites Enabled / Available
3.1 Default‑Deny (`/etc/nginx/sites-available/00-default-deny.conf`)
server {
listen 80 default_server;
server_name _;
access_log /var/log/nginx/scanners.log;
return 444;
}
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/imatrix.at/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/imatrix.at/privkey.pem;
access_log /var/log/nginx/scanners.log;
return 444;
}
3.2 imatrix.at (`/etc/nginx/sites-available/imatrix.at`)
map $http_pragma $nocache { default 0; "~*no-cache" 1; }
map $http_cache_control $nocache2 { default 0; "~*no-cache" 1; }
server {
listen 443 ssl http2;
server_name imatrix.at www.imatrix.at i-matrix.at www.i-matrix.at;
root /var/www/imatrix.at/public;
index index.php;
access_log /var/log/nginx/imatrix.access.log main;
error_log /var/log/nginx/imatrix.error.log warn;
include snippets/hardening.conf;
location ~* \.(css|js|jpg|jpeg|gif|png|svg|webp|ico|ttf|otf|woff|woff2)$ {
expires 30d;
access_log off;
}
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~* wp-config.php { deny all; }
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm-imatrix.sock;
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in_|comment_author|woocommerce_items_in_cart") { set $skip_cache 1; }
if ($nocache = 1) { set $skip_cache 1; }
if ($nocache2 = 1) { set $skip_cache 1; }
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
location = /wp-login.php {
limit_req zone=wpadmin burst=20 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm-imatrix.sock;
}
location ^~ /.well-known/acme-challenge/ {
allow all;
}
ssl_certificate /etc/letsencrypt/live/imatrix.at/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/imatrix.at/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
add_header Strict-Transport-Security "max-age=31536000" always;
}
server {
listen 80;
server_name imatrix.at www.imatrix.at i-matrix.at www.i-matrix.at;
return 301 https://$host$request_uri;
}
4. conf.d Layer
4.1 Cache (`/etc/nginx/conf.d/cache.conf`)
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2
keys_zone=WORDPRESS:100m inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
map $upstream_cache_status $cache_status {
default "";
HIT "HIT";
MISS "MISS";
BYPASS "BYPASS";
EXPIRED "EXPIRED";
}
add_header X-Cache $cache_status;
4.2 Security (`/etc/nginx/conf.d/security.conf`)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
limit_req_zone $binary_remote_addr zone=wpadmin:10m rate=10r/m;
5. Hardening Snippet (`/etc/nginx/snippets/hardening.conf`)
location ~* \.env(\.|$) {
access_log /var/log/nginx/scanners.log;
log_not_found off;
return 444;
}
location ~* ^/(phpinfo(\.php)?|app_dev\.php|_profiler)(/|$) {
access_log /var/log/nginx/scanners.log;
log_not_found off;
return 444;
}
location ~ /\.(?!well-known).* {
access_log /var/log/nginx/scanners.log;
log_not_found off;
return 444;
}
location = /.git {
access_log /var/log/nginx/scanners.log;
return 444;
}
location = /.svn {
access_log /var/log/nginx/scanners.log;
return 444;
}
location = /.hg {
access_log /var/log/nginx/scanners.log;
return 444;
}
location ~* \.(bak|old|orig|save|swp|swo|tmp)$ {
access_log /var/log/nginx/scanners.log;
log_not_found off;
return 444;
}
6. PHP‑FPM Pool (`/etc/php/8.2/fpm/pool.d/www.conf` – exemplarisch)
[www]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-imatrix.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500
process.priority = -19
process.dumpable = yes
php_admin_value[error_log] = /var/log/php8.2-fpm.log
php_admin_flag[log_errors] = on
7. Redis Konfiguration (`/etc/redis/redis.conf` – relevante Auszüge)
bind 127.0.0.1 -::1
protected-mode yes
port 6379
daemonize yes
pidfile /run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
databases 16
save 3600 1 300 100 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
8. Cache‑Flow in Worten (für dein Diagramm)
- Schritt 1:
Client sendet HTTP(S)‑Request an Nginx. - Schritt 2:
Nginx prüft: statische Datei? → Wenn ja, direkt ausliefern. - Schritt 3:
Wenn PHP‑Request: FastCGI‑Cache prüft Cache‑Key. - Schritt 4:
Wenn Cache‑HIT → Antwort direkt aus Cache an Client. - Schritt 5:
Wenn Cache‑MISS → Request an PHP‑FPM → WordPress. - Schritt 6:
WordPress nutzt Redis als Object‑Cache für DB‑Queries. - Schritt 7:
Antwort von WordPress zurück zu Nginx. - Schritt 8:
Nginx speichert Antwort (wenn nicht skip_cache) im FastCGI‑Cache. - Schritt 9:
Antwort geht an Client, inkl. X-Cache‑Header (HIT/MISS/BYPASS).