WordPress-Uploads absichern: PHP-Ausführung im Upload-Verzeichnis verhindern
PHP-Dateien im Ordner wp-content/uploads sperren: getestete .htaccess-Regel für Apache 2.4, nginx-Block aus der WordPress-Dokumentation, verbreitete Regeln, die im Test nicht wirkten, und der Test mit einer harmlosen Datei.
Mit KI erstellt – redaktionelle Prüfung ausstehend
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Mehr dazu

Der Ordner wp-content/uploads ist der einzige Bereich einer WordPress-Installation, in den der Webserver regelmäßig schreiben muss. Genau deshalb ist er ein beliebtes Ziel: Gelingt es einem Angreifer über eine Lücke in einem Plugin, dort eine PHP-Datei abzulegen, kann er sie über den Browser aufrufen und Befehle auf dem Server ausführen. In einem sauberen Upload-Ordner liegen nur Bilder, PDFs und andere Medien, PHP-Dateien haben dort nichts zu suchen. Diese Anleitung zeigt, wie Sie den Aufruf von PHP-Dateien im Upload-Verzeichnis unter Apache per .htaccess und unter nginx per Serverkonfiguration sperren, welche oft empfohlenen Regeln im Test nicht wirkten und wie Sie den Schutz prüfen. Getestet wurde mit WordPress 7.1.2 und Apache 2.4.
Voraussetzungen
- WordPress: jede aktuelle Version, getestet mit WordPress 7.1.2.
- Webserver: Apache 2.4 mit erlaubter
.htaccess(AllowOverridemindestens für Zugriffsregeln), wie bei den meisten Webhosting-Paketen, oder nginx mit Zugriff auf die Serverkonfiguration. Im Test lief Apache 2.4.68 mit PHP 8.4 als Apache-Modul. - Zugang: FTP-, SFTP- oder SSH-Zugang, um Dateien im Ordner
wp-content/uploadsanzulegen. Für nginx Root- oder Admin-Zugang zum Server. Ein Konto mit der Rolle Administrator im Backend für die Funktionskontrolle. - Backup: eine aktuelle Sicherung der Website, mindestens eine Kopie der vorhandenen
.htaccess-Dateien und der nginx-Konfiguration. - Werkzeug: ein Browser oder
curlfür den Test von außen.
Schritt 1: Ausgangslage prüfen
Stellen Sie zuerst fest, ob PHP im Upload-Ordner ausgeführt wird. Die WordPress-Dokumentation zu nginx beschreibt dafür einen einfachen Test: eine harmlose PHP-Datei in den Upload-Ordner legen und im Browser aufrufen. Legen Sie per FTP oder SSH eine Datei mit einer eindeutigen Ausgabe an:
cd /pfad/zu/wordpress
echo '<?php echo "PHP LAEUFT";' > wp-content/uploads/upload-test.php
curl -s -w " %{http_code}\n" https://www.example.com/wp-content/uploads/upload-test.php
Im Test auf einer frischen Installation lautete die Ausgabe PHP LAEUFT 200. Das bedeutet: Der Server führt PHP-Dateien im Upload-Ordner aus. WordPress selbst lässt über die Mediathek keine PHP-Dateien zu. Ein Import einer .php-Datei scheiterte im Test mit der Meldung, dass dieser Dateityp nicht hochgeladen werden darf. Der Schutz auf Serverebene ist trotzdem nötig, weil Angreifer nicht über die Mediathek kommen, sondern über Lücken in Plugins, die Dateien ohne diese Prüfung speichern.
Prüfen Sie außerdem, ob im Upload-Ordner schon PHP-Dateien liegen. Das sollte nicht der Fall sein:
find wp-content/uploads -type f -iname "*.php*"
Findet der Befehl Dateien, die Sie nicht selbst angelegt haben, ist die Website möglicherweise bereits kompromittiert. Dann gehen Sie vor wie in WordPress nach einem Hack bereinigen beschrieben, bevor Sie weitermachen.
Verifizieren: Sie wissen, ob die Testdatei ausgeführt wird (Ausgabe „PHP LAEUFT“ mit Status 200), und der Upload-Ordner enthält außer Ihrer Testdatei keine PHP-Dateien.
Schritt 2: Sperre unter Apache per .htaccess
Legen Sie im Ordner wp-content/uploads eine Datei mit dem Namen .htaccess an. Existiert dort bereits eine, etwa von einem Sicherheits-Plugin, sichern Sie sie und ergänzen Sie den folgenden Block am Ende:
<FilesMatch "\.(?i:php[0-9]?|phtml|phar)$">
Require all denied
</FilesMatch>
Der reguläre Ausdruck erfasst Dateien, die auf .php, .php mit einer Ziffer (etwa .php5), .phtml oder .phar enden. Der Zusatz ?i: macht die Prüfung unabhängig von Groß- und Kleinschreibung, sodass auch .PHP gesperrt ist. Require all denied ist die Schreibweise von Apache 2.4 aus dem Modul mod_authz_core. Die alte Form Order deny,allow und Deny from all stammt aus Apache 2.2 und funktioniert unter 2.4 nur mit dem Kompatibilitätsmodul.
Warum nicht einfach den ganzen Ordner sperren? Eine Zeile Require all denied ohne <FilesMatch> sperrt alle Dateien, auch Bilder. Im Test lieferte ein Bild danach HTTP 403, die Website hätte keine Medien mehr angezeigt.
Im Test ergab die Regel folgende Ergebnisse:
| Datei | Ohne Regel | Mit Regel |
|---|---|---|
test.php | 200, PHP ausgeführt | 403 |
test.PHP | nicht geprüft | 403 |
test.phtml, test.php5, test.phar | nicht geprüft | 403 |
bild.jpg | 200 | 200 |
Eine Unterordner-.htaccess mit Require all granted hob die Sperre im Test nicht auf, die Testdatei lieferte weiter 403. Laut Apache-Dokumentation werden <Files>- und <FilesMatch>-Abschnitte nach den Verzeichnisregeln und .htaccess-Dateien zusammengeführt. Verlassen Sie sich dennoch nicht darauf und prüfen Sie nach der Installation neuer Plugins mit find wp-content/uploads -name ".htaccess", ob neue .htaccess-Dateien entstanden sind.
Verifizieren: curl -s -o /dev/null -w "%{http_code}\n" https://www.example.com/wp-content/uploads/upload-test.php liefert 403, ein vorhandenes Bild aus der Mediathek liefert 200.
Schritt 3: Sperre unter nginx
nginx liest keine .htaccess-Dateien. Eine dort abgelegte Datei hat keine Wirkung, die Regel gehört in die Serverkonfiguration. Die WordPress-Dokumentation zu nginx enthält dafür diesen Block, der innerhalb des server-Blocks vor der allgemeinen PHP-Weitergabe stehen sollte:
# PHP-Dateien im Upload-Ordner sperren
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
Der Modifikator ~* vergleicht ohne Rücksicht auf Groß- und Kleinschreibung. Bei regulären location-Blöcken gewinnt der erste passende in der Reihenfolge der Konfiguration. Steht der Block hinter der allgemeinen Regel location ~ \.php$, greift er nicht. Wer auch .phtml und .phar abdecken möchte, erweitert das Ende des Ausdrucks auf \.(?:php[0-9]?|phtml|phar)$.
Prüfen Sie die Syntax und laden Sie die Konfiguration neu:
sudo nginx -t
sudo systemctl reload nginx
Die nginx-Variante wurde im Labor nicht getestet. Die Regel stammt aus der WordPress-Dokumentation. Führen Sie den Test aus Schritt 1 deshalb unbedingt durch. Im Webhosting ohne Zugriff auf die nginx-Konfiguration bitten Sie den Hoster, die Regel zu setzen, oder fragen Sie, ob der Upload-Ordner bereits geschützt ist.
Verifizieren: sudo nginx -t meldet eine gültige Syntax, und der Aufruf der Testdatei liefert HTTP 403.
Schritt 4: Unwirksame Regeln erkennen
In Foren und älteren Anleitungen kursieren Regeln, die nicht auf jedem Server wirken. Zwei davon wurden im Test geprüft, beide mit Ergebnis HTTP 200:
php_flag engine off: Die Zeile schaltet die PHP-Verarbeitung für den Ordner ab. Im Test wurde die Datei danach nicht mehr ausgeführt, Apache lieferte aber ihren Quelltext mit Status 200 aus. Das ist kein Angriffsweg mehr, legt aber Code offen. Außerdem ist php_flag eine Direktive des PHP-Apache-Moduls und steht nicht zur Verfügung, wenn PHP anders eingebunden ist, etwa über PHP-FPM. Nutzen Sie diese Regel deshalb nicht als Schutz.
Options -ExecCGI: Die Option betrifft CGI-Skripte, nicht PHP als Apache-Modul oder über PHP-FPM. Im Test lief die Testdatei weiter und gab „PHP LAEUFT“ aus.
Die Lehre daraus: Eine Regel ist erst dann ein Schutz, wenn der Test aus Schritt 1 eine Sperre zeigt. Die Variante mit <FilesMatch> und Require all denied verbietet den Zugriff, bevor PHP überhaupt beteiligt ist, und hängt deshalb nicht davon ab, wie PHP eingebunden ist.
Verifizieren: Ihre .htaccess im Upload-Ordner enthält keine der beiden unwirksamen Regeln als einzigen Schutz, und die Testdatei liefert 403.
Schritt 5: Testdatei entfernen und dauerhaft prüfen
Löschen Sie die Testdatei, sobald der Schutz bestätigt ist:
rm wp-content/uploads/upload-test.php
find wp-content/uploads -type f -iname "*.php*"
Der zweite Befehl sollte keine Ausgabe liefern. Nehmen Sie ihn in eine regelmäßige Kontrolle auf. Eine PHP-Datei im Upload-Ordner ist ein deutlicher Hinweis auf einen Angriff, auch wenn sie dank der Sperre nicht ausführbar ist.
Die Sperre ist eine von mehreren Schichten. Die WordPress-Dokumentation zur Härtung empfiehlt außerdem restriktive Dateirechte: Plugin- und Core-Dateien sollen nur für Ihr Benutzerkonto schreibbar sein, nur wp-content braucht Schreibrechte für den Webserver. Die Konstante DISALLOW_FILE_EDIT in der wp-config.php schaltet den Datei-Editor im Backend ab. Wie Sie Anmeldeversuche begrenzen, beschreibt Brute-Force-Angriffe auf wp-login.php abwehren.
Solche Regeln wirken nur, solange sie nach Server-Umzügen, Plugin-Wechseln und Updates noch vorhanden sind. Wer diese Kontrolle nicht selbst im Kalender halten möchte, kann sie abgeben: Die WordPress-Wartung von wordpressupdate.de richtet eine Firewall ein, pflegt sie und hält WordPress, Plugins und Themes mit Kompatibilitätsprüfung aktuell.
Verifizieren: Die Testdatei ist gelöscht, find liefert keine PHP-Dateien im Upload-Ordner, und die .htaccess beziehungsweise die nginx-Regel ist im Backup enthalten.
Typische Fehler
Alle Bilder verschwinden. Ursache ist eine .htaccess mit Require all denied ohne <FilesMatch>. Im Test lieferte ein Bild danach 403. Setzen Sie die Regel wie in Schritt 2 nur für PHP-Endungen.
Doppelte Endungen. Eine Datei test.php.jpg wurde im Test von der Regel nicht erfasst, weil sie auf .jpg endet. Apache lieferte sie mit 200 als Text aus, PHP führte sie nicht aus. Das ist das erwartete Verhalten bei der Standardkonfiguration des offiziellen Docker-Images, die nur \.php$ an PHP übergibt. Ältere Konfigurationen mit AddHandler können solche Dateien ausführen. Die Prüfung mit find … -iname "*.php*" findet auch diese Fälle.
Regel hat keine Wirkung. Unter Apache ist AllowOverride für den Ordner deaktiviert, oder der Server ist in Wahrheit nginx. Prüfen Sie den Header Server mit curl -I und fragen Sie gegebenenfalls den Hoster.
Serverfehler 500 nach dem Anlegen. Häufige Ursachen sind ein Tippfehler im Block oder eine Direktive, die der Server nicht kennt, etwa php_flag ohne PHP-Apache-Modul. Entfernen Sie die Datei, dann ist der alte Zustand wiederhergestellt.
Häufige Fragen
Brauchen Plugins PHP im Upload-Ordner?
Ordentlich programmierte Plugins legen dort keine ausführbaren PHP-Dateien ab. Manche speichern Cache- oder Konfigurationsdateien, die nur von WordPress gelesen und nicht per Browser aufgerufen werden. Die Sperre betrifft nur den Aufruf über den Webserver.
Überschreibt WordPress die .htaccess im Upload-Ordner?
WordPress verwaltet nur den Abschnitt zwischen # BEGIN WordPress und # END WordPress in der .htaccess im Stammverzeichnis. Die Datei im Upload-Ordner fasst WordPress nicht an. Sicherheits-Plugins können dort eigene Regeln schreiben.
Gilt das auch für Multisite?
Ja, Multisite speichert Uploads der Unterseiten in Unterordnern von wp-content/uploads/sites. Die .htaccess im übergeordneten Ordner gilt dort mit. Die nginx-Regel der Dokumentation deckt zusätzlich ältere Pfade mit files ab.
Ersetzt die Sperre ein Sicherheits-Plugin?
Nein, sie schließt einen bestimmten Angriffsweg. Updates, sichere Passwörter und Backups bleiben die Grundlage, siehe WordPress-Core-Updates sicher einspielen.
Testumfang
Wir haben die Regel in einer Testumgebung mit WordPress 7.1.2 und Apache ausprobiert und eine PHP-Testdatei vorher und nachher aufgerufen. Dabei haben wir auch verschiedene Dateiendungen, die Erreichbarkeit von Bildern und eine mögliche Aufhebung per Unterordner-.htaccess geprüft.
Nicht getestet haben wir nginx, PHP-FPM und hosterspezifische Konfigurationen. Läuft Ihre Seite in einer solchen Umgebung, testen Sie die Regel bitte zuerst auf einer Kopie.
Fazit
Eine .htaccess mit vier Zeilen verhindert unter Apache, dass PHP-Dateien im Upload-Ordner aufgerufen werden, ohne Bilder und Medien zu beeinträchtigen. Unter nginx gehört die Regel in die Serverkonfiguration. Entscheidend ist der Test mit einer echten Datei, denn verbreitete Regeln wie php_flag engine off wirkten im Test nicht wie erwartet. Wenn Sie solche Schutzmaßnahmen dauerhaft betreuen lassen möchten, übernimmt das eine WordPress-Wartung mit Firewall-Pflege.
Weiterführende Anleitungen und Quellen
- WordPress nach einem Hack bereinigen: Vorgehen ohne Datenverlust
- Brute-Force-Angriffe auf wp-login.php abwehren
- WordPress-Core-Updates sicher einspielen
- WordPress Advanced Administration: Nginx
- WordPress Advanced Administration: Hardening WordPress
- Apache 2.4: FilesMatch
- Apache 2.4: How the sections are merged
- Apache 2.4: Require (mod_authz_core)
- nginx: location


