Guides/Block PHP in uploads

WordPress security guide

How to block PHP in the WordPress uploads folder

The uploads folder is the one place WordPress lets the outside world write files. If a .php file lands there and the server runs it, the site is no longer yours.

Today, on most WordPress sites GET/wp-content/uploads/shell.php200 OK

A dropped script runs, with every permission WordPress has.


After this guide GET/wp-content/uploads/shell.php403 Forbidden

The file may be there; the server refuses to run it.

Why it matters

How takeovers endA vulnerable plugin or form accepts a file, the file is a small PHP script, the attacker requests its URL.
Any codeFrom that moment they can run anything on your server, as WordPress.
Nothing breaksImages, PDFs and fonts never need PHP, so refusing it in uploads costs nothing.

Check your site

This cannot be tested from outside without uploading a script, which is why the public check does not try. On your own server:

echo '<?php echo "php-runs";' > wp-content/uploads/nv-test.php
curl -s https://example.com/wp-content/uploads/nv-test.php
rm wp-content/uploads/nv-test.php

If you see php-runs, PHP executes in uploads. A 403 or 404, or the raw source, means it does not. Delete the test file either way.

Fix it

nginx

location ~* ^/wp-content/uploads/.*\.(php\d?|phtml|phar)$ {
    deny all;
}

Catch: nginx uses the first regular-expression location that matches, so this block must come before the general location ~ \.php$ block that hands requests to PHP.

Apache

Create wp-content/uploads/.htaccess:

<FilesMatch "\.(?i:php\d?|phtml|phar)$">
    Require all denied
</FilesMatch>

Catch: this needs AllowOverride to allow it for that folder; most shared hosts do. Run the test again to be sure.

Beyond uploads

Uploads is the obvious place, but a dropped script can also land in a plugin or theme folder. WordPress itself only needs a handful of PHP files reachable from the web: index.php, wp-login.php, wp-comments-post.php, wp-cron.php, and wp-admin. Refusing every other .php URL is stronger, but plugins that call their own PHP files directly need an exception, so try it in a log-only mode first.

Questions

Will this break image uploads?

No. Uploading goes through wp-admin, which is unaffected; only requesting a .php file inside the uploads folder is refused.

My host already blocks this. How do I know?

Run the three-line test above. Many hosts do block it, many do not, and it changes when a site moves.

Is this enough on its own?

It makes an uploaded script inert, which is the step most takeovers depend on. Keep plugins updated as well, since the upload itself still happened.

More guides

Last reviewed 2026-10-08