Back to blog
WordPress malware removal: step-by-step cleanup guide
WordPress

WordPress malware removal: step-by-step cleanup guide

ElevaSEOMarch 21, 202616 min read
wordpressmalwaresecuritycleanupwp-clihack

Your WordPress site has been hacked. The symptoms are there: redirects to malicious sites, spam pages injected into Google's index, unknown PHP files in wp-content, security alerts in Search Console, or worse, a red "This site is dangerous" warning greeting your visitors. Panic is understandable, but the solution demands method and rigor, not haste.

According to the Sucuri 2025 report, 95.6% of detected CMS infections involved WordPress. This number does not mean WordPress is fundamentally less secure than its competitors -- it reflects its dominant market share (43% of all websites) which makes it the most profitable target for attackers. The same report reveals that 61% of infected sites had not updated their CMS at the time of compromise.

This guide walks you through the complete cleanup process, from initial diagnosis to post-cleanup protection. Every step includes the exact CLI commands to execute. For WordPress security fundamentals, see our security guide. If your site was just hacked, also consult our guide on securing WordPress after a hack.

How to remove malware from WordPress (8 etapes)
  1. 1

    Isolate the site and create a backupPut the site in maintenance mode, back up all files and the database to a disconnected location for forensic preservation.

  2. 2

    Scan and identify the infectionUse WP-CLI, Wordfence CLI, and search commands to locate infected files, code injections, and backdoors.

  3. 3

    Reinstall WordPress coreDelete and reinstall WordPress core files with WP-CLI to eliminate any modifications to the base system.

  4. 4

    Clean plugins and themesReinstall all plugins and themes from official sources. Delete inactive and unrecognized extensions.

  5. 5

    Clean the databaseSearch for and remove SQL injections, fraudulent admin accounts, malicious options, and spam content.

  6. 6

    Verify and clean wp-config.php and .htaccessInspect these critical files for malicious modifications and restore them to a clean state.

  7. 7

    Change all credentialsModify all passwords, WordPress security keys, API keys, and FTP/SSH/database access credentials.

  8. 8

    Deploy post-cleanup protectionInstall a WAF, configure file monitoring, harden the configuration, and submit a review request to Google if necessary.

Phase 1: Diagnosis -- Identifying the Infection

Common symptoms of a WordPress infection

Before cleaning, you need to understand what you are facing. WordPress infections manifest in several ways, and the type of symptom guides the diagnosis. For a catalog of the most prevalent malware types, see our guide on common WordPress malware in 2026.

Malicious redirects: visitors are redirected to a third-party site (casino, pharma, malware). The redirect can be conditional (mobile only, first-time visitor only, Google referrer only), making it difficult for administrators to detect.

SEO spam injection (Japanese keyword hack): hundreds or thousands of pages are injected into Google's index, targeting keywords in Japanese, Chinese, or pharmaceutical terms. The administrator sees nothing on the site, but the pages appear in Search Console.

Backdoors: concealed PHP files allow the attacker to regain control of the site even after partial cleanup. Backdoors are often encoded in base64 or obfuscated with eval/gzinflate functions.

Cryptocurrency miners: JavaScript code is injected to mine cryptocurrency using visitors' CPU resources. The primary symptom is significant site slowdown and abnormal CPU usage.

Phishing pages: pages imitating banking sites or popular services are hosted on your server. These pages are often placed in discreet subdirectories within wp-content.

Scanning with WP-CLI and the command line

Diagnosis begins at the command line. Connect to your server via SSH and execute these commands:

Verify core file integrity:

wp core verify-checksums

This command compares every WordPress core file against the official release. Any modified or added file is flagged. If this command fails, the core is compromised.

Search for suspicious PHP files in wp-content/uploads:

find wp-content/uploads -name "*.php" -type f

The uploads directory should contain zero PHP files. Any PHP file in this directory is almost certainly malicious.

Search for dangerous functions:

grep -rn "eval(" wp-content/ --include="*.php"
grep -rn "base64_decode(" wp-content/ --include="*.php"
grep -rn "gzinflate(" wp-content/ --include="*.php"
grep -rn "str_rot13(" wp-content/ --include="*.php"
grep -rn "assert(" wp-content/ --include="*.php"

These functions are legitimately used in some plugins, but their presence in unknown files or unusual locations is an infection indicator.

List recently modified files:

find . -name "*.php" -mtime -7 -type f

This command lists PHP files modified in the last 7 days. Compare this list with your legitimate recent modifications.

Check for malicious cron jobs:

wp cron event list

Malware often installs cron jobs to re-download itself automatically after deletion.

Scanning with Wordfence CLI

If Wordfence is installed, leverage its scan capabilities:

wp wordfence scan --type=full

Wordfence compares files against official versions from the WordPress.org repository, detects known malware signatures, identifies malicious URLs in the database, and flags suspicious files outside expected locations.

Phase 2: Isolation and Backup

Put the site in maintenance mode

Before starting the cleanup, isolate the site to prevent infection spread and protect visitors:

wp maintenance-mode activate

Alternatively, create a .maintenance file at the root:

echo '<?php $upgrading = time(); ?>' > .maintenance

Back up the infected state

Counter-intuitively, you must back up the infected site before cleaning it. This backup serves for forensic analysis (understanding how the attack occurred), content recovery if cleanup is too aggressive, and potential legal or insurance procedures.

# File backup
tar -czf ~/backup-infected-$(date +%Y%m%d).tar.gz /var/www/html/
 
# Database backup
wp db export ~/backup-infected-$(date +%Y%m%d).sql

Store these backups in a location disconnected from the web server (external storage, local machine).

Phase 3: File Cleanup

Reinstall WordPress core

The most reliable method for cleaning the core is a complete reinstall:

# Download a clean copy of the core
wp core download --force --skip-content

This command downloads and overwrites all WordPress core files (wp-admin, wp-includes, root files) without touching wp-content. After reinstallation, re-verify integrity:

wp core verify-checksums

The result should be "WordPress installation verifies against checksums" with zero errors.

Clean wp-content

The wp-content directory is the most sensitive area because it houses your custom content and most infections. For understanding typical infection locations, see our guide on infected WordPress files.

Reinstall plugins from official sources:

# List all plugins
wp plugin list
 
# For each plugin, reinstall from the repository
wp plugin install plugin-name --force
 
# Delete inactive and unrecognized plugins
wp plugin delete suspicious-plugin-name

Reinstall themes:

# Reinstall the active theme from the repository
wp theme install theme-name --force
 
# Delete inactive themes
wp theme delete inactive-theme

Clean the uploads directory:

# Delete all PHP files in uploads
find wp-content/uploads -name "*.php" -type f -delete
 
# Search for suspicious file extensions
find wp-content/uploads -name "*.php5" -o -name "*.phtml" -o -name "*.shtml" -type f

Check hidden directories:

# Search for files in suspiciously named directories
find wp-content -type d -name ".*" | head -20
find wp-content -type d -name "cache" -o -name "tmp" -o -name "temp"

Malware often hides in hidden directories (starting with a dot) or in innocuously named directories (cache, tmp, temp, sessions).

Inspect wp-config.php

The wp-config.php file is a prime target because it contains database credentials and can be modified to execute malicious code:

# Check for suspicious functions
grep -n "eval\|base64_decode\|gzinflate\|str_rot13\|assert" wp-config.php

Manually verify the file and compare it against a clean wp-config.php. Elements to check:

  • No eval() or base64_decode() calls
  • Security keys are present and correctly formatted (generate new ones at https://api.wordpress.org/secret-key/1.1/salt/)
  • Table prefixes are correct
  • No suspicious file inclusions (require/include pointing to unusual paths)
  • Debug mode is disabled in production

Inspect .htaccess

The .htaccess file is the second most frequently modified file by malware:

cat .htaccess

A clean WordPress .htaccess contains only the standard rewrite rules:

# BEGIN WordPress
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress

Any additional redirect rules, any code between the # BEGIN and # END blocks, or any code before the WordPress block is suspicious. Replace the file with a clean version and regenerate permalinks:

wp rewrite flush

Phase 4: Database Cleanup

The database can contain malicious injections even after a complete file cleanup.

Search for injections in options

# Search for options containing suspicious content
wp db query "SELECT option_name, LEFT(option_value, 200) FROM wp_options WHERE option_value LIKE '%eval(%' OR option_value LIKE '%base64_decode(%' OR option_value LIKE '%<script%' LIMIT 50;"

Search for fraudulent administrator accounts

# List all administrators
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

Examine every administrator account. Any account you do not recognize or that was created recently (after the presumed infection date) must be deleted:

wp user delete USER_ID --reassign=1

Clean infected posts

# Search for malicious JavaScript in posts
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%eval(%' OR post_content LIKE '%<iframe%' OR post_content LIKE '%document.write(%' LIMIT 50;"

If posts are infected, you can clean them in bulk:

# Remove malicious script tags
wp db query "UPDATE wp_posts SET post_content = REGEXP_REPLACE(post_content, '<script[^>]*>.*?eval\\\(.*?</script>', '') WHERE post_content LIKE '%<script%eval(%';"

Clean malicious cron jobs

# List suspicious transients
wp db query "SELECT option_name FROM wp_options WHERE option_name LIKE '_transient_%' AND option_value LIKE '%eval(%' LIMIT 50;"
 
# Delete malicious transients
wp transient delete --all

Check additional tables

Some malware creates its own database tables:

# List all tables
wp db query "SHOW TABLES;"

Compare the list against standard WordPress tables (wp_posts, wp_options, wp_users, wp_usermeta, wp_comments, wp_commentmeta, wp_terms, wp_termmeta, wp_term_taxonomy, wp_term_relationships, wp_links). Additional tables come from plugins (verify the correspondence) or are potentially malicious.

Phase 5: Credential Rotation

After cleanup, change all credentials without exception:

WordPress passwords

# Change the password for each administrator
wp user update ADMIN_ID --user_pass="NewComplexPassword123!@#"

WordPress security keys

Regenerate all security keys in wp-config.php. Obtain new keys from:

curl https://api.wordpress.org/secret-key/1.1/salt/

Replace all 8 keys in wp-config.php (AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY, and their SALT variants). This change immediately invalidates all active sessions.

Database credentials

Change the MySQL user password in your hosting panel, then update wp-config.php with the new password.

FTP/SFTP and SSH access

Change FTP/SFTP passwords and SSH keys. If you use SSH keys, generate a new key pair and revoke the old ones.

Phase 6: Post-Cleanup Protection

Deploy a WAF (Web Application Firewall)

A WAF is your first line of defense against reinfection. It filters malicious traffic before it reaches your WordPress application. For a comprehensive WAF guide, see our article on WAF and web application firewalls.

The most effective options:

Cloudflare (free to 200 USD/month): DNS-level WAF with preconfigured rules for WordPress. The free plan offers basic protection; paid plans add advanced WAF rules.

Sucuri Firewall (199 USD/year): cloud WAF specialized for WordPress with rules specific to known WordPress vulnerabilities, cleanup included in case of reinfection.

Wordfence Premium (119 USD/year): application-level WAF installed directly on WordPress with a real-time firewall, malware scanner, and brute force protection. See our brute force protection guide for implementation details.

Configure file monitoring

Set up a system that alerts you immediately if files are modified:

# Create a reference checksum of current files
find /var/www/html -name "*.php" -exec md5sum {} \; > ~/checksums-reference.txt
 
# Daily verification script
find /var/www/html -name "*.php" -exec md5sum {} \; > ~/checksums-current.txt
diff ~/checksums-reference.txt ~/checksums-current.txt

Professional solutions (Wordfence, Sucuri, iThemes Security) automate this monitoring with email notifications.

Harden the configuration

Apply these hardening measures in wp-config.php:

// Disable the file editor in admin
define('DISALLOW_FILE_EDIT', true);
 
// Disable plugin/theme installation from admin
define('DISALLOW_FILE_MODS', true);
 
// Force SSL for admin
define('FORCE_SSL_ADMIN', true);

Update everything

# Update the core
wp core update
 
# Update all plugins
wp plugin update --all
 
# Update all themes
wp theme update --all

Enable automatic updates for minor security releases:

wp config set WP_AUTO_UPDATE_CORE minor

Request a Google review

If Google displayed a security warning on your site:

  1. Log in to Google Search Console
  2. Navigate to "Security Issues"
  3. Review the detected problems
  4. Once cleanup is complete, click "Request a Review"
  5. Describe the corrective actions taken

The review typically takes 24 to 72 hours. In the meantime, your site continues to display the warning.

Prevention: Avoiding the Next Infection

Prevention checklist

  • Automatic updates enabled for core, plugins, and themes
  • Active WAF with current rules
  • File monitoring enabled with alerts
  • Daily automated backups (files and database)
  • Strong, unique passwords for all accounts
  • Two-factor authentication (2FA) for all administrators
  • Correct file permissions (644 for files, 755 for directories)
  • XML-RPC disabled if not in use
  • Login attempt limits (brute force protection)
  • Unused plugins and themes deleted (not just deactivated)
  • Custom table prefix (not wp_)
  • Secure hosting with account isolation

Incident response plan

Prepare a response plan for future infections:

Who: designate a security lead (internal or external provider) with the necessary access and technical competency.

What: document the complete cleanup procedure (this guide can serve as a foundation) with commands specific to your environment.

When: define alert triggers (WAF notification, Search Console alert, user report, monitoring alert) and response timeframes (critical: 1 hour, important: 4 hours, normal: 24 hours).

How: store tools, scripts, and procedures in a location accessible even if the site is compromised (external Git repository, off-server documentation).

Understanding How the Infection Happened

Common attack vectors

Understanding the entry point is critical to preventing reinfection:

Vulnerable plugins account for approximately 56% of WordPress infections. Attackers scan for known vulnerabilities in outdated plugins and exploit them automatically. The risk is highest for plugins that have been abandoned by their developers or that have unpatched disclosed vulnerabilities.

Compromised credentials cause approximately 16% of infections. Weak passwords, reused passwords from breached databases, and brute force attacks on wp-login.php are the primary vectors. Hosting control panel credentials (cPanel, Plesk) are equally targeted.

Vulnerable themes represent approximately 10% of infections. Nulled (pirated) themes are particularly dangerous because they frequently contain pre-installed backdoors.

Core vulnerabilities account for approximately 2% of infections. While rare due to the WordPress security team's rapid response, they can be devastating when they occur because they affect all WordPress installations simultaneously.

Server-level compromises represent the remaining infections. Shared hosting environments where one compromised site can infect others on the same server, outdated PHP versions, and misconfigured server software are the typical causes.

Forensic analysis steps

After cleanup, invest time in understanding the attack:

# Check access logs for the attack timeline
grep "POST" /var/log/apache2/access.log | grep -v "wp-cron\|xmlrpc\|wp-admin/admin-ajax" | tail -100
 
# Search for the initial compromise
grep "wp-content/uploads.*\.php" /var/log/apache2/access.log | head -20
 
# Check for brute force attempts
grep "wp-login.php" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

These logs reveal the timeline of the attack, the initial entry point, and the attacker's behavior pattern, all of which inform your hardening strategy.

Related posts