How to Clean a Hacked WordPress Site (Step-by-Step)

Your WordPress site has been hacked. How to identify the malware, remove it completely, close the entry point, and verify clean operation.

Dobromir Dechev
Dobromir WordPress agency owner

A hacked WordPress site is stressful but recoverable. The key is acting methodically: identify the infection, remove it completely, close the entry point, and verify the cleanup. Skipping steps - especially identifying the entry point - means the malware comes back within days.

This guide covers the full process from initial detection through verified clean operation.


Step 1 - Confirm the site is actually hacked

Symptoms vary by attack type. Common signs:

  • Google Search Console warning: "This site may be hacked" or blacklisting notification
  • Browser security warnings (Google Safe Browsing, Chrome phishing warning)
  • Visitors being redirected to spam or pharma sites
  • Google index shows strange pages you did not create (/viagra-cheap-buy/, casino sites, etc.)
  • Admin account locked out or unknown admin accounts present
  • Hosting provider suspended the account for malware
  • Site visitors report seeing pop-ups or strange content
  • Site loads a blank page or white screen of death that appeared suddenly

Run the site through external scanners to get an outside view:

  • Sucuri SiteCheck (sitecheck.sucuri.net) - checks blacklist status and visible malware
  • Google Safe Browsing (transparencyreport.google.com/safe-browsing/search) - check if Google has flagged the domain
  • VirusTotal - check the domain against 70+ security vendors

These scanners only see what an external visitor sees. They will miss malware that only runs for logged-in users, malware hidden in database records, or backdoors that are not actively serving malicious content.


Step 2 - Take the site offline

Put the site in maintenance mode immediately. Leaving an infected site running:

  • Continues serving malware to visitors
  • Continues running the attacker's code (which may be mining cryptocurrency, sending spam, or attacking other sites)
  • Gives you a moving target for cleanup

If you have server access:

# Simple maintenance page via Nginx
location / {
    return 503;
}
error_page 503 /maintenance.html;

Or rename the site's document root temporarily and replace with a static HTML page.


Step 3 - Secure a backup before touching anything

Before you delete a single file, take a full backup of the compromised site. Even an infected backup is valuable:

  • It preserves your database, content, and media files
  • It lets you diff files later to find exactly what changed
  • It is your only option if cleanup damages the database
# Database backup
wp db export backup-infected-$(date +%Y%m%d).sql

# Files backup
tar czf backup-infected-$(date +%Y%m%d).tar.gz /var/www/html

Store these somewhere safe and clearly labelled as infected.


Step 4 - Identify the malware and entry point

Find modified files

Look for files changed recently (adjust the time window to when symptoms appeared):

# Files modified in the last 7 days
find /var/www/html -type f -newer /var/www/html/wp-config.php -name "*.php"

# PHP files modified in the last 30 days
find /var/www/html -name "*.php" -mtime -30 -ls | sort -k8

Look for common malware patterns

# Encoded/obfuscated PHP
grep -r --include="*.php" "base64_decode" /var/www/html
grep -r --include="*.php" "eval(" /var/www/html
grep -r --include="*.php" "gzinflate\|str_rot13\|gzuncompress" /var/www/html

# PHP in uploads (should never be there)
find /var/www/html/wp-content/uploads -name "*.php"

# Malicious iframe injections
grep -r "iframe" /var/www/html/wp-content/themes --include="*.php"

Many of these patterns appear in legitimate plugins too (serialized data, compressed scripts). Context matters - look at the full file, not just the matched line.

Check the database for injected content

wp db search "base64_decode" --all-tables
wp db search "<script" --all-tables
wp db search "eval(" --all-tables

Injected JavaScript in post content, widget content, or options is common. Attackers often inject into the wp_options table (siteurl, home, or custom options they add).

Check for backdoors

A backdoor lets the attacker re-enter after cleanup. Common locations:

  • wp-content/uploads/ - PHP files disguised as images
  • wp-content/plugins/ - extra PHP files added to legitimate plugins
  • Theme files - extra functions in functions.php
  • wp-content/mu-plugins/ - must-use plugins that load automatically
# PHP files in uploads (all should be treated as suspicious)
find /var/www/html/wp-content/uploads -name "*.php" -o -name "*.phtml"

# Filenames that look like WordPress core but are not
find /var/www/html -name "wp-*.php" | grep -v "wp-content\|wp-admin\|wp-includes"

Step 5 - Clean WordPress core files

Download a fresh copy of WordPress matching your current version:

wp core download --force --version=$(wp core version)

The --force flag overwrites all core files. This replaces any modified core files with clean originals. It does not touch wp-content/ or wp-config.php.

Verify the checksums:

wp core verify-checksums

Any files that fail checksum verification were modified. If wp-settings.php or wp-load.php is modified, that is a strong indicator of a serious compromise.


Step 6 - Clean plugins and themes

The safest approach: delete all plugins and themes, then reinstall from official sources.

# Remove all plugins
rm -rf /var/www/html/wp-content/plugins/*

# Reinstall from WordPress.org
wp plugin install [plugin-slug] --activate

# For premium plugins - reinstall from vendor

For premium plugins you cannot reinstall via WP-CLI, download fresh copies from the vendor and upload via SFTP.

Do not restore plugins from your infected backup. The backup may contain the original malware.

For themes: delete all themes except the one in active use, then reinstall the active theme from scratch. If it is a custom theme, you need to carefully review each file.


Step 7 - Clean the database

Database cleanup is the most sensitive step - mistakes can lose content.

Remove injected content from posts:

# Find posts with injected script tags
wp post list --field=ID | xargs -I {} wp post get {} --field=post_content | grep -n "<script"

For widespread injection, use a plugin like Better Search Replace to find and replace malicious strings across all database tables. Run it in dry-run mode first.

Check and clean wp_options:

# List all options - look for unfamiliar ones
wp option list

# Check siteurl and home are correct
wp option get siteurl
wp option get home

# Remove malicious options
wp option delete [suspicious-option-name]

Remove unknown admin accounts:

wp user list --role=administrator

Delete any accounts you did not create.


Step 8 - Change all credentials

After cleanup, assume all credentials are compromised:

  1. WordPress admin passwords - reset all admin accounts
  2. Database password - change in MySQL and update wp-config.php
  3. WordPress auth keys and salts - regenerate (logs out all sessions)
  4. Hosting control panel password
  5. FTP/SFTP credentials - disable FTP entirely if possible; use SFTP only
  6. SSH keys - if you suspect SSH compromise, regenerate keys and remove unauthorised ones from ~/.ssh/authorized_keys

Step 9 - Identify and close the entry point

Cleanup without closing the entry point means the same attacker (or the same automated bot) re-infects within hours.

Check your server access logs for the time period before symptoms appeared:

# Nginx
grep "POST" /var/log/nginx/access.log | grep "2025-01-20\|2025-01-21"

# Look for suspicious POSTs to PHP files in unusual locations
grep "uploads.*\.php" /var/log/nginx/access.log

Common entry points:

  • Vulnerable plugin or theme - check WPScan database for CVEs matching your installed versions
  • Compromised admin account - check for logins from unexpected IPs
  • Weak FTP/SFTP password - check FTP access logs
  • Compromised hosting account - contact your host if other sites on the account are also infected

Once identified, update the vulnerable plugin/theme, or block the specific exploit with a WAF rule.


Step 10 - Verify and monitor

After cleanup:

  1. Run wp core verify-checksums - all should pass
  2. Run a Wordfence or Sucuri scan
  3. Re-check Google Safe Browsing status (can take 1-3 days to clear after cleanup)
  4. If Google blacklisted the site, request a review through Search Console
  5. Set up file change monitoring so you are alerted immediately if files change again

Monitor access logs daily for the first week post-cleanup. A returning attacker often tries the same entry point again.


When to call a professional

If the infection is widespread (hundreds of modified files, heavily obfuscated malware, database-level injection across all content), professional cleanup is faster and more reliable than manual cleaning. Sucuri and Wordfence both offer guaranteed malware removal services. The cost of professional cleanup is almost always less than the revenue lost from extended downtime or continued spam penalties.


Was this article helpful?