One of the most common real-world WordPress compromise routes: a malicious PHP file, disguised as an image or hidden inside a legitimate-looking upload, gets placed into wp-content/uploads/ through a form, a vulnerable plugin, or a compromised account, then requested directly by URL to actually execute. WordPress core has never needed PHP execution inside this folder, it’s meant for media, not code, and WordPress.org’s own hardening guide lists disabling PHP execution in the uploads directory as a specific recommended step, so blocking it there closes off an entire category of exploit with no functional downside for a normal site.
Method 1: a security plugin
Most WordPress security plugins offer this as a single toggle, handling the server configuration correctly behind the scenes without you needing to edit any files directly. On SecondGate, this sits within the Pro tier’s site hardening features (£39.99/year) rather than the free tier. This is the recommended approach for most site owners since it removes any risk of a manual configuration mistake breaking something else.
Method 2: Apache, using .htaccess
Create or edit a .htaccess file specifically inside wp-content/uploads/ (not your site’s root .htaccess) with the following:
<FilesMatch "\.(php|php3|php4|php5|phtml)$">
Order Deny,Allow
Deny from all
</FilesMatch>
This uses Apache’s standard FilesMatch directive to deny any request matching a PHP file extension within this specific folder, regardless of what the file actually contains. Save it, and any direct request to a .php file inside uploads/ should now return a 403 Forbidden error rather than executing.
Method 3: Nginx configuration
Nginx doesn’t use .htaccess files, this needs to go into your site’s server block configuration instead, using Nginx’s standard location directive, typically requiring server access or a request to your host if you’re on shared hosting without direct config access:
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
Add this within your site’s server block and reload Nginx (sudo systemctl reload nginx on most setups) for the change to take effect. Since Nginx configuration changes require a reload to apply, unlike Apache’s .htaccess which takes effect immediately, double-check the syntax is valid before reloading, a malformed config block can prevent Nginx from restarting at all.
Verify it actually worked
Upload a harmless test PHP file (something as simple as <?php echo 'test'; ?>) directly into wp-content/uploads/ via FTP or your host’s file manager, then try to access it directly in a browser (yoursite.com/wp-content/uploads/test.php). Before the block, you’d see “test” printed on the page, confirming the file executed. After a correctly configured block, you should get a 403 Forbidden error or similar, confirming the server refused to run it. Delete the test file once you’ve confirmed the block works.
Check that legitimate uploads still work normally
This change only affects PHP execution, actual image, document, and media uploads through the normal WordPress media library should be entirely unaffected. Test uploading and displaying a normal image after making this change, just to confirm nothing else in the configuration was accidentally broken alongside the intended block.
Where this fits into a broader security setup
This closes one specific, well-defined attack route, and it pairs naturally with keeping plugins and themes updated, since an outdated, vulnerable upload handler is typically how a malicious file ends up in that folder in the first place. For the complete picture, how to secure a WordPress site covers this alongside everything else that matters, and if you’re addressing this after already discovering an infection, how to remove malware from a WordPress site covers the cleanup itself.






