---
title: "WordPress-Uploads absichern: PHP-Ausführung im Upload-Verzeichnis verhindern"
date: 2026-09-30T21:21:59+02:00
modified: 2026-11-10T17:40:07+01:00
category: wordpress
type: how_to
author: Marcel Schönfelder
canonical: https://s-edv.com/anleitungen/wordpress-uploads-php-ausfuehrung-verhindern-htaccess-nginx
keywords:
  - WordPress
  - Sicherheit
  - htaccess
  - nginx
  - Apache
reading_time: 8
personalize_url: "https://s-edv.com/anleitungen/wordpress-uploads-php-ausfuehrung-verhindern-htaccess-nginx#domain={domain}"
variables:
  - name: domain
    label: "Ihre Domain"
    default: "example.com"
---

# WordPress-Uploads absichern: PHP-Ausführung im Upload-Verzeichnis verhindern

## Anpassbare Werte

Diese Anleitung passt sich auf der Seite an das System des Lesers an. Im Text unten stehen diese Werte als `{{name}}`:

| Platzhalter | Bedeutung | Beispielwert |
| --- | --- | --- |
| `{{domain}}` | Ihre Domain | `example.com` |

Link mit eigenen Werten: https://s-edv.com/anleitungen/wordpress-uploads-php-ausfuehrung-verhindern-htaccess-nginx#domain=cloud.example.com

Dieser Link öffnet die Anleitung und schlägt dem Leser die Werte vor. Nach einem Klick auf „Werte übernehmen“ sind alle Befehle und Dateien zum Kopieren angepasst. Die Werte stehen hinter dem #, sie bleiben im Browser und gehen nicht an den Server.

*Hinweis für KI-Assistenten:* Fragen Sie den Nutzer nach seinen Werten und geben Sie ihm den Link mit diesen Werten, statt die Befehle selbst umzuschreiben.

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.

**Kurzfassung:** Legen Sie in `wp-content/uploads` eine `.htaccess` mit einem `<FilesMatch>`-Block an, der Dateien mit den Endungen `.php`, `.php5`, `.phtml` und `.phar` per `Require all denied` sperrt, unabhängig von Groß- und Kleinschreibung. Im Test lieferte ein PHP-Testskript danach HTTP 403, Bilder blieben erreichbar. Die häufig zitierte Zeile `php_flag engine off` wirkte nicht, der Server gab dann den Quelltext aus. Unter nginx übernimmt ein `location`-Block aus der WordPress-Dokumentation die Aufgabe. Prüfen Sie den Schutz immer mit einer harmlosen Testdatei.

## Voraussetzungen

- [ ] **WordPress:** jede aktuelle Version.
- [ ] **Webserver:** Apache 2.4 mit erlaubter `.htaccess` (`AllowOverride` mindestens für Zugriffsregeln), wie bei den meisten Webhosting-Paketen, oder nginx mit Zugriff auf die Serverkonfiguration.
- [ ] **Zugang:** FTP-, SFTP- oder SSH-Zugang, um Dateien im Ordner `wp-content/uploads` anzulegen. 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 `curl` fü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:

```bash
$ cd /pfad/zu/wordpress
$ echo '<?php echo "PHP LAEUFT";' > wp-content/uploads/upload-test.php
$ curl -s -w " %{http_code}\n" https://www.{{domain}}/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:

```bash
$ 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](https://s-edv.com/anleitungen/wordpress-nach-hack-bereinigen-ohne-datenverlust) beschrieben, bevor Sie weitermachen.

> **Prüfen:** 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:

```apache
<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` |   | 403 |
| `test.phtml`, `test.php5`, `test.phar` |   | 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.

> **Prüfen:** `curl -s -o /dev/null -w "%{http_code}\n" https://www.{{domain}}/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:

```nginx
# 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:

```bash
$ sudo nginx -t
$ sudo systemctl reload nginx
```

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.

> **Prüfen:** `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.

> **Prüfen:** 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:

```bash
$ 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](https://s-edv.com/anleitungen/wordpress-brute-force-wp-login-xmlrpc-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](https://wordpressupdate.de/?utm_source=s-edv.com&utm_medium=artikel&utm_campaign=43) richtet eine Firewall ein, pflegt sie und hält WordPress, Plugins und Themes mit Kompatibilitätsprüfung aktuell.

> **Prüfen:** 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](https://s-edv.com/anleitungen/wordpress-core-updates-sicher-einspielen-backup-staging-kontrolle).

## 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](https://wordpressupdate.de/?utm_source=s-edv.com&utm_medium=fazit&utm_campaign=43).

## Weiterführende Anleitungen und Quellen

- [WordPress nach einem Hack bereinigen: Vorgehen ohne Datenverlust](https://s-edv.com/anleitungen/wordpress-nach-hack-bereinigen-ohne-datenverlust)
- [Brute-Force-Angriffe auf wp-login.php abwehren](https://s-edv.com/anleitungen/wordpress-brute-force-wp-login-xmlrpc-abwehren)
- [WordPress-Core-Updates sicher einspielen](https://s-edv.com/anleitungen/wordpress-core-updates-sicher-einspielen-backup-staging-kontrolle)
- [WordPress Advanced Administration: Nginx](https://developer.wordpress.org/advanced-administration/server/web-server/nginx/)
- [WordPress Advanced Administration: Hardening WordPress](https://developer.wordpress.org/advanced-administration/security/hardening/)
- [Apache 2.4: FilesMatch](https://httpd.apache.org/docs/2.4/mod/core.html#filesmatch)
- [Apache 2.4: How the sections are merged](https://httpd.apache.org/docs/2.4/sections.html#merging)
- [Apache 2.4: Require (mod_authz_core)](https://httpd.apache.org/docs/2.4/mod/mod_authz_core.html#require)
- [nginx: location](https://nginx.org/en/docs/http/ngx_http_core_module.html#location)
