WordPress-Probleme selbst beheben
Schritt-für-Schritt-Lösungen für die Probleme, die WP Monitor meldet. Erstellen Sie vor jeder Änderung ein vollständiges Backup (Dateien und Datenbank). Serverregeln stehen jeweils für Apache (in der Datei .htaccess im Hauptverzeichnis; LiteSpeed liest dieselbe Datei) und für nginx (in der Website-Konfiguration; nginx danach neu laden).
WordPress aktualisieren
Veraltetes WordPress ist der häufigste Weg, über den Websites gehackt werden. Sicherheitskorrekturen erscheinen nur für die aktuelle Version; eine Version mit bekannter Sicherheitslücke sollte noch am selben Tag aktualisiert werden.
- Im Adminbereich: Dashboard > Aktualisierungen, dann „Auf Version … aktualisieren“.
- Danach die Website prüfen: Startseite, einige Seiten, das Kontaktformular und, falls vorhanden, den Shop-Checkout.
- Lassen Sie automatische Unterversions-Updates eingeschaltet (Standard). Sie installieren Sicherheitsupdates selbstständig.
Mit Shell-Zugang erledigt WP-CLI dasselbe im Website-Ordner:
wp core update
wp core update-db
Plugins und Themes aktualisieren
Plugins und Themes sind der zweite häufige Angriffsweg: Die meisten WordPress-Sicherheitsmeldungen betreffen sie.
- Unter Plugins > Installierte Plugins und Design > Themes alles aktualisieren, wofür ein Update bereitsteht.
- Löschen Sie nicht genutzte Plugins und Themes. Ein deaktiviertes Plugin liegt weiterhin auf dem Server, und seine Dateien können weiterhin angegriffen werden.
- Premium-Plugins und -Themes erhalten Updates nur mit gültiger Lizenz. Ist sie abgelaufen, verlängern Sie sie oder ersetzen Sie das Plugin.
- Erwägen Sie automatische Updates für Plugins, denen Sie vertrauen („Automatische Aktualisierungen aktivieren“ in der Plugin-Liste).
wp plugin update --all
wp theme update --all
Seiten-Cache aktivieren
Ohne Seiten-Cache baut WordPress jede Seite bei jedem Besuch aus PHP und Datenbank neu. Ein Seiten-Cache speichert die fertige Seite und liefert sie direkt aus – meist um ein Vielfaches schneller und deutlich belastbarer bei Besucherspitzen.
- WP Rocket (kostenpflichtig): läuft auf jedem Server, mit guten Voreinstellungen.
- W3 Total Cache (kostenlos): viele Optionen; wählen Sie beim Seiten-Cache „Disk: Enhanced“.
- WP-Optimize (kostenlos): Seiten-Cache plus Datenbankbereinigung, einfach einzurichten.
- Läuft Ihr Hosting auf dem LiteSpeed-Webserver, nutzen Sie stattdessen LiteSpeed Cache: Es verwendet den eingebauten Cache des Servers. Auf Apache oder nginx ist es eingeschränkt, dort besser eines der Plugins oben.
Verwenden Sie immer nur ein Seiten-Cache-Plugin. Manche Hoster (SiteGround, Kinsta, WP Engine u. a.) cachen Seiten bereits auf dem Server; dann ist dafür kein Plugin nötig.
Nach obenKomprimierung aktivieren
Komprimierung (Gzip oder Brotli) macht Seiten, CSS und JavaScript auf dem Weg zum Besucher 60–80 % kleiner. Die Cache-Plugins oben können sie einschalten, oder sie wird in der Serverkonfiguration gesetzt:
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml
AddOutputFilterByType DEFLATE application/javascript text/javascript application/json
AddOutputFilterByType DEFLATE application/xml application/rss+xml image/svg+xml
</IfModule>
# in the http { } or server { } block
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types text/plain text/css text/xml application/javascript text/javascript
application/json application/xml application/rss+xml image/svg+xml;
CSS und JavaScript im Browser zwischenspeichern
Werden CSS, JavaScript, Bilder und Schriften mit langer Cache-Dauer ausgeliefert, laden wiederkehrende Besucher und jede weitere Seite sie aus dem Browser statt neu herunter. WordPress versieht diese Dateien mit einer Version (?ver=...), die sich bei Änderungen ändert – eine lange Dauer ist also unbedenklich.
Die meisten Cache-Plugins haben eine Option „Browser-Caching“. Oder auf dem Server einstellen:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
# in the server { } block
location ~* \.(?:css|js|webp|jpe?g|png|gif|svg|ico|woff2?)$ {
expires 30d;
access_log off;
try_files $uri =404;
}
nginx: Ein location-Block mit eigenen add_header-Zeilen übernimmt die add_header-Zeilen des server-Blocks nicht mehr. Wiederholen Sie dort gegebenenfalls die Sicherheits-Header.
Die erste Antwort beschleunigen
Das ist die Zeit, bis die Seite zu laden beginnt, gemessen von unserem Server aus. Über 1,5 Sekunden bedeutet meist, dass die Seite bei jedem Besuch neu erzeugt wird.
- Aktivieren Sie einen Seiten-Cache (siehe oben). Das behebt es meist schon.
- Ist bereits ein Cache aktiv, suchen Sie ein langsames Plugin: Ein Plugin wie Query Monitor zeigt, welche Plugins Seiten verlangsamen.
- Nutzen Sie eine aktuelle PHP-Version (8.2 oder neuer) mit aktiviertem OPcache.
- Hilft das alles nicht, ist der Hosting-Tarif vermutlich zu klein für die Website.
WordPress-Emoji-Skript deaktivieren
WordPress fügt in den <head> jeder Seite ein kleines Skript und CSS ein, um Emojis in Browsern, die sie nicht darstellen können, als Bilder zu zeichnen. Alle aktuellen Browser zeigen Emojis selbst an, es ist also nur zusätzlicher Code bei jedem Seitenaufruf.
Viele Optimierungs-Plugins haben eine Option „Emojis deaktivieren“ (Autoptimize, LiteSpeed Cache, Perfmatters u. a.), und das kleine Plugin „Disable Emojis“ macht nur das. Oder fügen Sie diesen Code ein:
// functions.php of a child theme, or a small custom plugin
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_enqueue_scripts', 'wp_enqueue_emoji_styles');
remove_action('wp_print_styles', 'print_emoji_styles');
add_filter('emoji_svg_url', '__return_false');
- Setzen Sie den Code in die
functions.phpeines Child-Themes oder in ein kleines eigenes Plugin. Nicht ins Haupt-Theme: Ein Theme-Update würde ihn entfernen. - Emojis in Ihren Beiträgen funktionieren weiter: Browser zeigen sie als normale Textzeichen an.
- Leeren Sie danach den Seiten-Cache und prüfen Sie: Seitenquelltext öffnen (Strg+U) und nach „wp-emoji“ suchen. Es darf nicht gefunden werden.
SSL-Zertifikat reparieren
Ist das Zertifikat für die Adresse nicht gültig, sehen Besucher statt der Website eine ganzseitige Browserwarnung („Ihre Verbindung ist nicht privat“), Suchmaschinen werfen sie aus dem Index, und Formulare und Anmeldungen sind nicht sicher. Was zu tun ist, hängt vom Befund ab:
- Zertifikat für einen anderen Namen: Der Server antwortet mit einem Zertifikat für andere Namen, oft dem Standardzertifikat des Hosters. Die Website hat dort kein eigenes Zertifikat: Richten Sie eines für example.com und www.example.com ein.
- Abgelaufen: Erneuern Sie es. Kostenlose Let’s-Encrypt-Zertifikate gelten 90 Tage und müssen sich automatisch erneuern; ist eines abgelaufen, funktioniert die automatische Erneuerung nicht.
- Selbstsigniert oder nicht vertrauenswürdig: Browser vertrauen nur Zertifikaten einer öffentlichen Zertifizierungsstelle. Ersetzen Sie es durch ein Let’s-Encrypt- (kostenlos) oder kommerzielles Zertifikat und installieren Sie die vollständige Kette („CA-Bundle“ bzw. „fullchain“), nicht nur das Zertifikat selbst.
- Gar kein HTTPS: Installieren Sie ein Zertifikat und leiten Sie http:// danach auf https:// um.
Auf Shared Hosting über das Control Panel: AutoSSL in cPanel, „SSL It!“ oder Let’s Encrypt in Plesk, oder fragen Sie den Hoster. Auf dem eigenen Server holt und erneuert certbot Let’s-Encrypt-Zertifikate:
# free Let's Encrypt certificate, renewed automatically (run as root)
certbot --nginx -d example.com -d www.example.com # nginx
certbot --apache -d example.com -d www.example.com # Apache
Sobald HTTPS funktioniert, leiten Sie http:// auf https:// um:
# .htaccess, above the # BEGIN WordPress block
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
- Prüfen: Öffnen Sie die Website in einem privaten Fenster mit und ohne www. Es muss ein Schloss erscheinen und keine Warnung.
Alle http://-Adressen auf https:// umleiten
Jede Adresse der Website sollte Besucher von http:// auf https:// weiterleiten, nicht nur die Startseite. Übernimmt WordPress die Umleitung (weil seine Adresse unter Einstellungen > Allgemein mit https:// beginnt, oder per Plugin), gilt sie nur für die Seiten, die WordPress selbst ausliefert: 404-Seiten, robots.txt und andere Dateien öffnen sich weiterhin unverschlüsselt und laden oft auch ihre Skripte und Stylesheets über http://. Scanner wie Sucuri SiteCheck melden diese Seiten. Eine Serverregel erfasst jede Adresse und ist zudem schneller: Der Besucher wird umgeleitet, bevor WordPress überhaupt lädt.
Viele Hoster bieten dafür einen Schalter: „Force HTTPS Redirect“ in cPanel (Domains), „Permanente, SEO-sichere 301-Weiterleitung von HTTP zu HTTPS“ in Plesk (Hosting-Einstellungen) oder „Always Use HTTPS“ in Cloudflare (SSL/TLS > Edge Certificates). Andernfalls fügen Sie die Regel selbst hinzu:
# .htaccess, above the # BEGIN WordPress block
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
- Hinter Cloudflare im SSL-Modus „Flexible“ oder einem anderen Proxy, der Ihren Server über http:// anspricht, erzeugt die Apache-Regel eine Umleitungsschleife. Stellen Sie Cloudflare auf „Full (strict)“ um oder nutzen Sie statt der Regel den Schalter „Always Use HTTPS“.
- Leeren Sie danach den Seiten-Cache (LiteSpeed Cache, WP Rocket ...): Er kann noch Seiten enthalten, die über http:// gespeichert wurden.
- Prüfen:
curl -I http://ihre-website/eine-fehlende-seitemuss mit301undLocation: https://...antworten.
XML-RPC deaktivieren
xmlrpc.php ist eine alte Fernzugriffs-Schnittstelle. Angreifer probieren darüber Hunderte Passwörter in einer einzigen Anfrage. Die meisten Websites brauchen sie nicht mehr; das Jetpack-Plugin und manche Publishing-Apps schon – dann überspringen Sie diesen Schritt.
<Files "xmlrpc.php">
Require all denied
</Files>
location = /xmlrpc.php {
deny all;
}
Auch Sicherheits-Plugins (Wordfence, Solid Security u. a.) können XML-RPC deaktivieren.
Nach obenWordPress-Generator-Tag entfernen
Standardmäßig schreibt WordPress seine genaue Version in jede Seite: <meta name="generator" content="WordPress 7.1.2">, und in den RSS-Feed: <generator>https://wordpress.org/?v=7.1.2</generator>. Das verrät Angreifern, welche bekannten Lücken sie probieren können.
Entfernen Sie es mit der Option „WordPress-Version verbergen“ eines Sicherheits-Plugins (Wordfence, Solid Security u. a. haben eine) oder mit einem Must-Use-Plugin. Ein Must-Use-Plugin ist eine PHP-Datei im Ordner wp-content/mu-plugins: WordPress lädt sie automatisch, sie muss nicht aktiviert werden, kann im Adminbereich nicht versehentlich deaktiviert werden, und Theme- und Plugin-Updates lassen sie unberührt.
Legen Sie den Ordner mu-plugins in wp-content an, falls er noch nicht existiert (per FTP oder im Dateimanager Ihres Hostings), und darin eine Datei hide-wp-version.php mit diesem Inhalt:
<?php
// Plugin Name: Hide WordPress version
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
- Legen Sie die Datei direkt in
mu-pluginsab, nicht in einen Unterordner: Dateien in Unterordnern lädt WordPress dort nicht. Im Adminbereich erscheint sie dann unter Plugins > Must-Use. - Statt eines Must-Use-Plugins können Sie die letzten beiden Zeilen (ohne
<?php) in diefunctions.phpeines Child-Themes einfügen – nicht ins Haupt-Theme, ein Theme-Update würde sie entfernen. - Leeren Sie danach den Seiten-Cache, sonst erhalten Besucher weiter die alten Seiten mit dem Tag.
- Prüfen: Startseite öffnen, Quelltext anzeigen (Strg+U) und nach „generator“ suchen. Dort darf nicht mehr „WordPress“ stehen.
- Prüfen Sie auch den RSS-Feed:
https://ihre-website/feed/öffnen und nach „generator“ suchen. Der oft geteilte Einzeilerremove_action('wp_head', 'wp_generator')entfernt das Tag nur aus den Seiten; die Zeile mitthe_generatoraus der Datei oben entfernt es auch aus dem Feed.
Zugriff auf readme.html sperren
Jede WordPress-Installation hat eine Datei readme.html im Hauptverzeichnis, und sie zeigt jedem, der sie öffnet, die WordPress-Version.
Löschen hält nicht lange: WordPress legt die Datei bei jedem Core-Update neu an. Sperren Sie stattdessen den Zugriff auf dem Server; die Regel bleibt nach Updates bestehen.
Apache und LiteSpeed: Fügen Sie dies in die Datei .htaccess im Hauptverzeichnis ein (der Ordner mit wp-config.php), oberhalb oder unterhalb des Blocks # BEGIN WordPress, nicht darin (WordPress schreibt diesen Block neu):
<Files "readme.html">
Require all denied
</Files>
nginx: Fügen Sie dies in den server { }-Block der Website ein, dann die Konfiguration prüfen und neu laden (nginx -t, dann systemctl reload nginx). Ein exakter location =-Treffer hat Vorrang vor den anderen Regeln:
location = /readme.html {
deny all;
}
- Prüfen: Öffnen Sie
https://ihre-website/readme.html. Sie sollten „403 Forbidden“ statt der WordPress-Readme-Seite sehen. - Auf Shared Hosting ohne Zugriff auf die Serverkonfiguration nutzen Sie die
.htaccess-Regel; der Dateimanager von cPanel oder Plesk kann sie bearbeiten („versteckte Dateien anzeigen“ aktivieren).
wp-admin/upgrade.php sperren
/wp-admin/upgrade.php ist die Seite, die nach einem Core-Update die WordPress-Datenbank aktualisiert. Jeder kann sie ohne Anmeldung öffnen, und ihr Quelltext zeigt die genaue WordPress-Version. WordPress lädt auf dieser Seite keine Plugins, daher erreicht die Option „Version verbergen“ eines Sicherheits-Plugins sie nicht.
Normale Updates brauchen sie nicht: Updates über Dashboard > Aktualisierungen, automatische Updates und wp core update-db (WP-CLI) aktualisieren die Datenbank selbst. Sperren Sie sie auf dem Server:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^wp-admin/upgrade\.php$ - [F,L]
</IfModule>
Setzen Sie die Apache-Regel über den Block # BEGIN WordPress, nicht hinein (WordPress schreibt diesen Block neu).
location = /wp-admin/upgrade.php {
deny all;
}
- Prüfen: Öffnen Sie
https://ihre-website/wp-admin/upgrade.phpin einem privaten Browserfenster. Sie sollten „403 Forbidden“ sehen. - Falls Sie WordPress einmal selbst per FTP aktualisieren und der Adminbereich danach ein Datenbank-Update verlangt: Regel kurz entfernen, Update ausführen, Regel wieder einfügen.
Benutzernamen nicht auflisten
Standardmäßig listet /wp-json/wp/v2/users die Benutzernamen aller auf, die Inhalte veröffentlicht haben – die halbe Anmeldung für Angreifer. Dieser Code verbirgt die Liste vor nicht angemeldeten Besuchern; der Block-Editor funktioniert weiter:
// functions.php of a child theme, or a small custom plugin
add_filter('rest_endpoints', function ($endpoints) {
if (!is_user_logged_in()) {
unset($endpoints['/wp/v2/users'], $endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});
Die meisten Sicherheits-Plugins haben eine ähnliche Option („User Enumeration deaktivieren“).
Der RSS-Feed zeigt bei jedem Beitrag den Namen des Autors (<dc:creator>), und WordPress setzt diesen Namen auf den Benutzernamen, solange der Autor keinen anderen gewählt hat. Dafür ist kein Code nötig: Tragen Sie unter Benutzer > Profil für jeden Autor einen Spitznamen ein, der vom Benutzernamen abweicht, und wählen Sie ihn bei „Öffentlicher Name“ aus. Die Autorenzeile der Beiträge und die Autorenseiten zeigen dann ebenfalls diesen Namen.
Verzeichnisauflistung deaktivieren
Mit aktiver Verzeichnisauflistung kann jeder die Dateiliste eines Ordners wie /wp-content/uploads/ durchsehen, einschließlich privater Uploads und Backups.
Options -Indexes
# nginx lists directories only where autoindex is switched on: remove it, or set
autoindex off;
Öffentliches Debug-Log entfernen
wp-content/debug.log kann Serverpfade, Plugin-Fehler und manchmal Datenbankdetails enthalten, und jeder kann die Datei herunterladen. Löschen Sie sie jetzt und verhindern Sie, dass WordPress sie dort erneut schreibt:
// wp-config.php
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
// or, to keep logging, write the log outside the website folder:
// define('WP_DEBUG_LOG', '/home/USER/logs/wp-debug.log');
Und sperren Sie den Dateinamen, falls das Logging später wieder eingeschaltet wird:
<Files "debug.log">
Require all denied
</Files>
location ~* /debug\.log$ {
deny all;
}
Apache-, nginx- und PHP-Version verbergen
Antwort-Header wie Server: nginx/1.30.5 oder X-Powered-By: PHP/8.2.12 verraten Angreifern genau, für welche Versionen sie bekannte Sicherheitslücken nachschlagen können. Der Servername allein (nginx, Apache) ist unbedenklich; verbergen sollten Sie die Versionsnummer. Das ersetzt keine Updates, es hört nur auf, die Version anzukündigen.
Apache: in der Haupt-Serverkonfiguration (in .htaccess nicht möglich). Auf Shared Hosting fragen Sie Ihren Hoster; die meisten tun das bereits.
# main Apache configuration (not .htaccess), e.g. /etc/apache2/conf-enabled/security.conf
# or /etc/httpd/conf/httpd.conf, then reload Apache
ServerTokens Prod
ServerSignature Off
nginx: im http { }-Block. fastcgi_hide_header entfernt auch den PHP-Versions-Header:
# in the http { } block of nginx.conf, then: nginx -t && systemctl reload nginx
server_tokens off;
# hides the PHP version header coming from PHP-FPM
fastcgi_hide_header X-Powered-By;
PHP: verhindern, dass PHP X-Powered-By überhaupt sendet. expose_php lässt sich nur in php.ini oder im PHP-FPM-Pool setzen, nicht in .user.ini:
; php.ini (or the PHP-FPM pool: php_admin_flag[expose_php] = off), then restart PHP-FPM
expose_php = Off
Ohne Zugriff auf php.ini (Shared Hosting mit Apache oder LiteSpeed) entfernen Sie den Header stattdessen in .htaccess:
# .htaccess: removes the PHP version header when you cannot change php.ini
<IfModule mod_headers.c>
Header always unset X-Powered-By
Header unset X-Powered-By
</IfModule>
- Prüfen:
curl -I https://ihre-website/sollteServer: nginxoderServer: Apacheohne Nummer zeigen und keine ZeileX-Powered-By: PHP/....
Sicherheits-Header ergänzen
Drei Antwort-Header schließen verbreitete Angriffe im Browser:
Strict-Transport-Security(HSTS): Browser nutzen für Ihre Website immer HTTPS, auch bei eingetipptem oder verlinktem http://. Erst setzen, wenn HTTPS auf der ganzen Website funktioniert.X-Frame-Options: Andere Websites können Ihre Seiten nicht in einem Frame anzeigen (Clickjacking).X-Content-Type-Options: Browser raten keine Dateitypen, was einige Tricks zum Einschleusen von Skripten verhindert.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
</IfModule>
# in the server { } block that serves HTTPS
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;