Key Principle for Beginners
WordPress security is not about installing more tools. It is about removing places where malicious code can execute.
Most real-world WordPress attacks do not require administrator access. Instead, attackers upload PHP files into writable directories and repeatedly execute them from there. If those execution paths are removed, reinfection usually stops.
- Reduce execution paths
- Reduce privilege exposure
- Protect configuration files
- Create a clean restore point
Site Context Used in This Guide
- WordPress running on shared hosting
- WordPress root directory typically public_html
- Changes made through hosting File Manager and wp-admin
- No SSH or terminal access required
If your hosting uses a different web root, replace public_html with the correct directory.
What the Attack Looked Like
The incident that led to this hardening process revealed that malicious files were being placed in writable server directories rather than directly inside theme or plugin files.
WordPress functioned mainly as an entry point, while the malicious scripts attempted to execute from writable folders and temporary server locations.
This is why the hardening strategy focuses on blocking execution paths rather than simply deleting malware files.
Step 1 — Secure the Root .htaccess
Location
public_html/.htaccess
Code
# Disable directory listing
Options -Indexes
# Protect wp-config.php
<Files wp-config.php>
Require all denied
</Files>
# Block XML-RPC completely
<Files xmlrpc.php>
Require all denied
</Files>
# Block access to hidden or backup files
<FilesMatch "^\\.(env|git|ht|svn)|\\.(bak|backup|old|sql)$">
Require all denied
</FilesMatch>
Purpose
- Prevents directory listing
- Blocks direct access to configuration files
- Disables XML-RPC attack surface
- Protects backup and environment files
Step 2 — Block PHP Execution in Writable Folders
Create a .htaccess file in the following directories:
public_html/wp-includes/
public_html/wp-content/uploads/
public_html/wp-content/plugins/
public_html/wp-content/cache/
Code
<FilesMatch "\.php$">
Require all denied
</FilesMatch>
Even if a malicious PHP file appears in these folders, the server will refuse to execute it.
Step 3 — Remove Abused or Unused Folders
Example folder discovered during investigation:
public_html/wp-admin/maint/
If a directory like this is not required by WordPress, delete it using the hosting file manager.
Unused folders often become storage locations for malicious scripts.
Step 4 — Harden wp-config.php
Location
public_html/wp-config.php
Settings
define('DISALLOW_FILE_EDIT', true);
define('FORCE_SSL_ADMIN', true);
define('WP_DEBUG', false);
These settings:
- Disable the WordPress file editor
- Force secure admin connections
- Prevent debugging information from leaking
Step 5 — Minimal Theme Hardening
Location
public_html/wp-content/themes/YOUR-THEME/functions.php
// Remove WordPress version from head
remove_action('wp_head', 'wp_generator');
// Disable REST API user enumeration
add_filter('rest_endpoints', function ($endpoints) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
return $endpoints;
});
// Disable XML-RPC pingbacks
add_filter('xmlrpc_methods', function ($methods) {
unset($methods['pingback.ping']);
return $methods;
});
This reduces automated discovery and fingerprinting.
Step 6 — Remove Public Information Files
Delete if present:
public_html/readme.html
public_html/license.txt
These files may reveal WordPress version information to automated scanners.
Step 7 — Correct File Permissions
Folders: 755
Files: 644
wp-config.php: tighter permissions if supported
This limits what compromised scripts can modify on the server.
Step 8 — Audit WordPress Users
Check user accounts in:
wp-admin → Users
Actions taken:
- Verified a single administrator
- Confirmed legitimate author account
- Removed unnecessary subscriber accounts
Step 9 — Rotate Database Credentials
Change the database user password in the hosting control panel and update it inside:
public_html/wp-config.php
define('DB_PASSWORD', 'NEW_PASSWORD_HERE');
This prevents database access using previously exposed credentials.
Step 10 — Create a Final Backup
Create a full backup including files and database after confirming the site is clean.
Example backup label:
post-hardening-clean-state
Additional Hardening — API Tokens and FTP Accounts
Server-level access credentials can bypass WordPress security entirely. After any security incident, review these access methods.
API Tokens
Many hosting panels, cloud services, and automation tools use API tokens for authentication.
Recommended actions:
- Review all existing API tokens in the hosting control panel
- Delete tokens that are no longer required
- Regenerate tokens that may have been exposed
- Store tokens securely and avoid sharing them through email or plain text
Rotating API tokens prevents automated systems from continuing to access your server using previously issued credentials.
FTP Accounts
FTP access allows direct modification of site files and can bypass WordPress login security.
Review FTP accounts in the hosting control panel:
- Remove unused FTP users
- Change passwords for active accounts
- Use strong unique passwords
- If possible, restrict FTP access to specific directories
Reducing unnecessary FTP accounts removes another potential entry point.
What This Guide Did Not Do
- No security plugins
- No server shell commands
- No cron manipulation
- No continuous malware scanning
The goal was to reduce the attack surface rather than add operational complexity.
Final Outcome
- Execution paths for malicious scripts restricted
- Configuration files protected
- Unused folders removed
- User privileges verified
- Database credentials rotated
- Server access tokens and FTP accounts reviewed
- A clean backup state created
The result is a stable and hardened WordPress installation requiring minimal ongoing maintenance.