Incorrect file permissions and an unprotected uploads directory are two of the most exploited weaknesses on WordPress servers. If an attacker can upload a PHP file — through a vulnerable plugin, a weak password, or a misconfigured server — and the web server is allowed to execute PHP in the uploads folder, they have achieved remote code execution. The WordPress uploads folder (wp-content/uploads/) should never execute PHP: it stores images, PDFs, and other media, and there is no legitimate reason for any PHP file to be there. Blocking PHP execution in uploads via an .htaccess file is a one-line fix that eliminates an entire class of webshell attacks. File permissions follow a simple rule: directories should be 755 (owner can read/write/execute; group and others can read/execute) and files should be 644 (owner can read/write; group and others can only read). wp-config.php deserves extra protection at 440 or 400 since no web process needs to write to it. The web server user (www-data on Ubuntu/Debian, apache on CentOS) should own the WordPress files so WordPress can write to wp-content/ for plugin updates, but this ownership should be as narrow as possible. World-writable files (777) are a red flag — they mean any process on the server can modify that file. The find command makes it easy to audit and bulk-correct permissions across the entire WordPress installation. Review this alongside the HTTP security headers guide and the wp-config.php protection guide for a layered security approach.
Problem: Your WordPress uploads folder allows PHP execution, and your file permissions may be too permissive — both are common entry points for malware and webshell attacks.
Solution: Add an .htaccess file to the uploads directory to block PHP, and run the find commands below to set correct permissions across the entire WordPress installation:
# Set directory permissions to 755
find /var/www/html/wordpress/ -type d -exec chmod 755 {} \;
# Set file permissions to 644
find /var/www/html/wordpress/ -type f -exec chmod 644 {} \;
# Lock down wp-config.php — only the owner can read it
chmod 440 /var/www/html/wordpress/wp-config.php
# Verify: find any world-writable files (should return nothing)
find /var/www/html/wordpress/ -perm -o+w -type f
# Check web server ownership (adjust user to match your server)
chown -R www-data:www-data /var/www/html/wordpress/wp-content/
# Place this file at wp-content/uploads/.htaccess
# Blocks execution of PHP, Perl, and Python files in the uploads folder
<Files "*.php">
Require all denied
</Files>
<Files "*.php5">
Require all denied
</Files>
<Files "*.phtml">
Require all denied
</Files>
<Files "*.pl">
Require all denied
</Files>
# Also disable script execution via AddHandler
<FilesMatch "\.(php|php5|phtml|pl|py|cgi)$">
SetHandler default-handler
</FilesMatch>
NOTE: After placing the .htaccess file in wp-content/uploads/, test it by trying to access a .php file in that directory directly via the browser — you should receive a 403 Forbidden response. On Nginx, add location ~* /wp-content/uploads/.*\.php$ { deny all; } to your server block instead, since Nginx does not read .htaccess files. Also check that your hosting environment does not automatically recreate a permissive .htaccess in uploads — some managed hosts do this. Combine this with the XML-RPC disable guide and username enumeration prevention for a hardened WordPress setup.