When a WordPress site gets hacked, most people rush straight into cleanup mode. I've watched hundreds of tickets where site owners delete infected files, change passwords, and declare victory—only to see the same malware reappear within days.
The pattern repeats because cleanup mistakes are predictable. You miss a backdoor, skip a log check, or trust a plugin to do what you should verify manually. Each error leaves an opening.
Mistake 1: Starting cleanup before taking a backup
People panic and start deleting files immediately. Bad move.
You need a snapshot of the compromised state for two reasons: forensic analysis if the infection spreads, and a rollback point if you accidentally break something during cleanup. I've seen admins delete core WordPress files thinking they were malware, then realize they nuked their entire installation with no way back.
Take a full backup before touching anything. Use your hosting control panel backup tool, run a manual cPanel backup, or pull everything via SFTP and mysqldump. Store it outside the server—local storage or a different cloud account.
# Manual backup via SSH
tar -czf ~/backup-$(date +%F).tar.gz /home/username/public_html
mysqldump -u dbuser -p dbname > ~/backup-$(date +%F).sql
Once you have a clean snapshot, proceed.
Mistake 2: Trusting security plugins to find everything
WordPress security plugins are useful. They're not magic.
Wordfence, Sucuri, iThemes Security—they scan file checksums, look for known malware signatures, and flag suspicious patterns. But sophisticated attackers obfuscate code, hide payloads in database fields, or plant backdoors in places plugins don't check by default. I've pulled infected sites where Wordfence showed green while a web shell sat in an obscure uploads subdirectory.
Use plugins as a starting point, not the final answer. After a plugin scan, manually inspect:
- Recently modified files (sort by timestamp)
- wp-content/uploads for PHP files (should never be there)
- Theme and plugin directories for unfamiliar code
- Database wp_posts, wp_options, and wp_users tables for base64 blobs or eval() calls
Grep for common malware patterns:
grep -r "eval(base64_decode" /home/username/public_html/
grep -r "system(\$_GET" /home/username/public_html/
grep -r "passthru" /home/username/public_html/
Compare results against what the plugin reported. Gaps mean hidden threats.
Mistake 3: Only checking file timestamps from the day you noticed the hack
The day you discovered the malware is not the day it arrived.
Most infections sit dormant for weeks or months. An attacker compromises a site, plants a backdoor, waits, then triggers a second-stage payload later. If you only look at files modified in the last 24 hours, you miss the original entry point.
Pull a list of all files modified in the past 90 days and sort them:
find /home/username/public_html -type f -mtime -90 -ls | sort -k9
Look for:
- PHP files in wp-content/uploads
- Hidden files starting with a dot (.config.php, .htaccess in odd places)
- Files with timestamps that don't match plugin/theme update schedules
- Anything in wp-includes that changed recently (core files should only change during WordPress updates)
Cross-reference modification dates with your site's update history. If wp-admin/admin-ajax.php changed two months ago but you didn't update WordPress, investigate.
Mistake 4: Leaving old themes and plugins installed
Deactivated doesn't mean safe.
An inactive theme or plugin is still executable code sitting on your server. Attackers scan for known vulnerabilities in every installed component, active or not. If you have a 2019 version of an abandoned plugin in wp-content/plugins, it's an open door.
Delete everything you're not using. Every theme except your active one and maybe one fallback. Every plugin that isn't enabled and necessary. Go through your plugins directory and ask: do I actually use this? If the answer is no or you don't remember installing it, delete it.
After removal, update everything that remains. Check for plugin updates in the WordPress dashboard and run them. For themes, verify the active theme is current. If a plugin has no updates available and hasn't been updated in over a year, find a maintained alternative.
Mistake 5: Not rotating every credential after cleanup
Changing your admin password isn't enough.
If malware had access to your site, assume it logged every credential it could reach: database passwords, FTP accounts, SFTP keys, wp-config.php salts, hosting control panel logins, email accounts tied to the domain. I've seen cases where attackers re-entered through an unchanged FTP password days after the site owner cleaned everything.
Rotate all of these:
- WordPress admin passwords for every user
- Database password in wp-config.php and in the hosting control panel
- FTP/SFTP passwords and delete any accounts you don't recognize
- cPanel or Plesk password
- Email account passwords (especially the admin email in WordPress settings)
- Any API keys stored in wp-config.php or plugin settings
Update wp-config.php salts:
define('AUTH_KEY', 'new-value-from-api.wordpress.org/secret-key/1.1/salt/');
define('SECURE_AUTH_KEY', 'new-value');
define('LOGGED_IN_KEY', 'new-value');
define('NONCE_KEY', 'new-value');
// ... and the corresponding SALT constants
Generate fresh salts from the WordPress API and replace the existing ones. This invalidates all active sessions, including the attacker's.
Mistake 6: Skipping database cleanup
Malware doesn't only live in files.
Attackers inject code into database tables—especially wp_posts (as fake posts or pages), wp_options (in active plugins or theme settings), and wp_users (rogue admin accounts). A file-only cleanup leaves database-based backdoors untouched.
Check for:
- Admin users you didn't create (run a query for user_level 10 or role administrator)
- Suspicious entries in wp_options, particularly siteurl, home, active_plugins, and any keys starting with an underscore
- Posts or pages with base64-encoded content or JavaScript you don't recognize
- Unfamiliar cron jobs in wp_options under cron
Query the database:
SELECT * FROM wp_users WHERE user_login NOT IN ('yourknownadmin');
SELECT * FROM wp_options WHERE option_value LIKE '%eval(%';
SELECT * FROM wp_posts WHERE post_content LIKE '%base64%' AND post_status != 'trash';
Delete any rows that don't belong. For wp_options, if you're unsure what an entry does, search the key name—legitimate plugins usually have documentation.
Mistake 7: Restoring from a backup without verifying it's clean
Rolling back to a backup sounds safe. It's only safe if the backup predates the infection.
Most people don't know when malware first arrived, so they restore last week's backup—and reinfect the site with the same payload. I've seen three-cycle loops where someone restores, gets hacked again within hours, restores again, and still can't figure out why.
Before restoring, check the backup date against your earliest evidence of compromise. Look at access logs, file timestamps, and any alerts from your security plugin. If the backup was taken after the infection started, it's contaminated.
If you must use a recent backup because older ones don't exist, restore it to a staging environment first. Run a fresh scan with updated malware signatures, manually inspect suspicious files, and check database tables. Only migrate it to production once you've confirmed it's clean.
Mistake 8: Ignoring server-level compromises
Sometimes the problem isn't WordPress.
If your entire server or hosting account is compromised—say, through an outdated cPanel version, weak SSH password, or another site on the same account—cleaning WordPress alone won't help. The attacker has server access and will reinfect your site from outside.
Check server logs for unauthorized access:
# SSH login attempts
grep "Failed password" /var/log/auth.log | tail -50
grep "Accepted password" /var/log/auth.log | tail -50
# FTP access (if running pure-ftpd or vsftpd)
tail -100 /var/log/xferlog
Look for login attempts from unfamiliar IPs or successful logins you don't recognize. If you find evidence of server-level access, you need to secure the entire environment: change the root password, rotate SSH keys, disable password authentication for SSH, update control panel software, and check every site on the server.
On shared hosting, contact your provider. If one account is compromised, they need to isolate it and scan the server.
Mistake 9: Not hardening the site after cleanup
Cleaning malware puts you back to square one. Square one is where you got hacked.
Without changing how the site is secured, you're waiting for the next infection. Most site owners stop at cleanup and don't address the vulnerability that let attackers in. Within a month, they're dealing with the same issue.
Harden your installation:
- Block PHP execution in wp-content/uploads with an .htaccess rule:
<FilesMatch "\.php$">
Order Allow,Deny
Deny from all
</FilesMatch>
- Move wp-config.php one directory above public_html if your hosting setup allows it
- Set correct file permissions: 644 for files, 755 for directories, 600 for wp-config.php
- Enable two-factor authentication for all admin accounts
- Install a firewall plugin (Wordfence or Cloudflare WAF) and enable brute-force protection
- Disable file editing from the WordPress admin by adding
define('DISALLOW_FILE_EDIT', true);to wp-config.php - Set up automated daily backups to offsite storage
- Enable automatic updates for minor WordPress releases and plugins
Monitor access logs for unusual patterns. Set up alerts for failed login spikes or unexpected admin activity.
What about reinfection within hours?
If malware returns immediately after cleanup, you missed a backdoor.
The attacker still has access. Go back through steps two and three—manually search for web shells, hidden PHP files, and database-injected code. Check your access logs to see what's being hit. Often a reinfection happens because a secondary backdoor in an obscure directory is triggering the reinstall.
Grep your access logs for POST requests to unusual files:
grep "POST" /home/username/access_logs/yourdomain.com | grep ".php" | tail -100
Any POST to a file you don't recognize is worth investigating. Download it, examine the code, and delete it if malicious.
FAQ
How do I know if my backup is infected?
Check the backup's creation date against the earliest sign of compromise in your logs or file timestamps. If the backup is newer than the earliest modified malware file, assume it's dirty. Restore to a test environment and scan before using it in production.
Can I just reinstall WordPress and keep my database?
Yes, but scan your database first. Reinstalling core files and plugins is fast and eliminates file-based malware, but database-injected backdoors will persist. Run the database queries in mistake six before wiping files.
Do I need to tell my hosting provider?
If you're on shared hosting and suspect server-level compromise, yes. Otherwise, it's optional but often helpful—support can check server logs, review recent account activity, and confirm your cleanup was complete.
Should I change my domain registrar password too?
Yes. If attackers had deep access to your hosting account, they may have logged credentials stored in your browser or config files, including registrar logins. Rotate those as well.
First step when malware appears
Take a backup before you delete anything. Then scan with a plugin, verify results manually, and work through the checklist: check file timestamps beyond today, remove unused themes and plugins, rotate all credentials, clean the database, and confirm your backup isn't contaminated.
Most cleanup failures come from rushing or trusting automated tools to catch everything. Slow down, verify each step, and harden the site once you're done. That's how you stop the cycle.
