---
title: "Remove WordPress Malware Manually: Technical Guide"
description: "Technical guide to manually remove WordPress malware. SSH commands, PHP file cleanup, database cleaning and hardening."
date: "2026-03-11"
author: "Bastien Allain"
category: "SEO"
tags: ["wordpress", "malware", "manual-removal", "security", "wp-cli"]
keywords: ["wordpress", "malware", "manual-removal", "security", "wp-cli"]
image: "/blog/category-seo.webp"
published: true
locale: "en"
translationKey: "wordpress-manual-malware-removal"
imageAlt: "Remove WordPress Malware Manually: Technical Guide"
lastmod: "2026-03-11"
authorImage: "/authors/bastien-allain.webp"
authorTwitter: ""
isFamilyFriendly: true
---

When a WordPress site is infected, the most reliable solution is often **manual cleanup**. Automated security plugins do not always detect sophisticated backdoors or obfuscated injections. This technical guide walks you through the step-by-step process of manually removing WordPress malware, using **SSH commands**, **SQL queries** and **WP-CLI** to restore a clean and secure site. Every command is documented so you can intervene with precision, even on complex infections.

## Prerequisites and Environment Preparation

Before starting the cleanup, you need to prepare your working environment. A poorly prepared intervention can worsen the situation or cause data loss.

### Required Tools

| Tool | Usage | Access |
|------|-------|--------|
| **SSH** | Command-line server access | Terminal / PuTTY |
| **SFTP** | Secure file transfer | FileZilla / WinSCP |
| **phpMyAdmin** | Database administration | Hosting panel |
| **WP-CLI** | WordPress command-line interface | Server installation |
| **Text editor** | File analysis | nano / vim / VS Code |

### Create a Full Backup

The absolute first step is to **back up the entire site**, even if infected. You will need this backup as a reference if the cleanup goes wrong.

```bash
# SSH into your server
ssh user@your-server.com

# Create a backup directory
mkdir -p ~/backups/$(date +%Y%m%d)

# Back up all WordPress files
tar -czf ~/backups/$(date +%Y%m%d)/files-backup.tar.gz /var/www/html/

# Back up the database
mysqldump -u db_user -p db_name > ~/backups/$(date +%Y%m%d)/database-backup.sql
```

### Put the Site in Maintenance Mode

To prevent visitors from accessing the infected site and block malware propagation:

```bash
# Create a .maintenance file at the WordPress root
echo '<?php $upgrading = time(); ?>' > /var/www/html/.maintenance
```

## Step 1: Identify Malicious Files

Detection is the most critical step. WordPress malware uses obfuscation techniques to evade standard scans. Here are the **essential grep commands** to locate suspicious files.

### Search for Dangerous PHP Functions

```bash
# Search for eval() with base64_decode - classic malware signature
grep -rl "eval(base64_decode" /var/www/html/wp-content/ --include="*.php"

# Search for common obfuscation functions
grep -rl "str_rot13\|gzinflate\|gzuncompress\|str_replace.*chr(" /var/www/html/wp-content/ --include="*.php"

# Search for dangerous system calls
grep -rl "exec(\|system(\|passthru(\|shell_exec(\|popen(" /var/www/html/wp-content/ --include="*.php"

# Search for remote file inclusions
grep -rl "file_get_contents.*http\|curl_exec\|wp_remote_get.*base64" /var/www/html/wp-content/ --include="*.php"
```

### Detect Recently Modified Files

Malware often modifies existing files, as detailed in our analysis of [the most targeted WordPress files](/blog/seo/infected-wordpress-files). Search for recently changed files:

```bash
# PHP files modified in the last 7 days
find /var/www/html/ -name "*.php" -mtime -7 -type f

# PHP files modified in the last 24 hours
find /var/www/html/ -name "*.php" -mtime -1 -type f

# Files with suspicious names (random characters)
find /var/www/html/wp-content/ -name "*.php" -regex ".*[a-z0-9]\{8,\}\.php"
```

### Identify Files That Do Not Belong to Core

```bash
# Download a clean copy of WordPress
cd /var/tmp && wget https://wordpress.org/latest.tar.gz && tar -xzf latest.tar.gz

# Compare core files with the clean version
diff -rq /var/www/html/wp-admin/ /PATH/TO/CLEAN-WP/wp-admin/
diff -rq /var/www/html/wp-includes/ /PATH/TO/CLEAN-WP/wp-includes/
```

Files present in your installation but absent from the clean version are potentially **injected malicious files**.

## Step 2: Clean WordPress Core Files

The WordPress core (the `wp-admin/` and `wp-includes/` directories) should never be modified. The safest method is to **replace them entirely**.

### Replace wp-admin and wp-includes

```bash
# Remove existing core directories
rm -rf /var/www/html/wp-admin/
rm -rf /var/www/html/wp-includes/

# Copy clean files
cp -r /PATH/TO/CLEAN-WP/wp-admin/ /var/www/html/wp-admin/
cp -r /PATH/TO/CLEAN-WP/wp-includes/ /var/www/html/wp-includes/

# Copy root core files (except wp-config.php)
cp /PATH/TO/CLEAN-WP/wp-*.php /var/www/html/
cp /PATH/TO/CLEAN-WP/index.php /var/www/html/
cp /PATH/TO/CLEAN-WP/xmlrpc.php /var/www/html/
```

### Clean wp-config.php

The `wp-config.php` file is a prime target for attackers. Inspect it line by line:

```bash
# Display wp-config.php contents
cat -n /var/www/html/wp-config.php
```

Search for and remove:

- Any code **before** `<?php` at the beginning of the file
- Lines containing `eval()`, `base64_decode()`, `str_rot13()`
- `require()` or `include()` pointing to unknown files
- Unexpected WordPress constants added at the end of the file

```bash
# Check for suspicious inclusions in wp-config.php
grep -n "eval\|base64_decode\|require.*\.php\|include.*\.php" /var/www/html/wp-config.php
```

**Regenerate security keys** by replacing the `AUTH_KEY`, `SECURE_AUTH_KEY`, etc. lines with fresh values generated at [api.wordpress.org/secret-key](https://api.wordpress.org/secret-key/1.1/salt/).

### Clean .htaccess

The `.htaccess` file is frequently used for **malicious redirects**:

```bash
# Display .htaccess contents
cat -n /var/www/html/.htaccess
```

Look for:

- `RewriteRule` directives redirecting to external domains
- `<FilesMatch>` blocks hiding PHP files
- `php_value auto_prepend_file` instructions loading malicious code

Replace the content with the default WordPress `.htaccess`:

```apache
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
```

### Clean index.php

The root `index.php` file should only contain:

```php
<?php
/**
 * Front to the WordPress application.
 */
define( 'WP_USE_THEMES', true );
require __DIR__ . '/wp-blog-header.php';
```

Any additional code is suspicious and should be removed.

## Step 3: Clean the wp-content Directory

The `wp-content/` directory contains your themes, plugins and uploads. This is the most complex area to clean because it mixes **legitimate third-party code** with malware.

### Clean Plugins

```bash
# List all PHP files in plugins
find /var/www/html/wp-content/plugins/ -name "*.php" | head -50

# Search for malicious code in plugins
grep -rl "eval(base64_decode\|eval(gzinflate\|eval(str_rot13" /var/www/html/wp-content/plugins/ --include="*.php"
```

The most reliable method: **delete and reinstall** each plugin from the official WordPress repository:

```bash
# With WP-CLI - reinstall all plugins from the repository
wp plugin list --path=/var/www/html --format=csv | tail -n +2 | while IFS=',' read name status update version; do
  wp plugin install "$name" --force --path=/var/www/html
done
```

### Clean Themes

Apply the same logic to themes:

```bash
# Reinstall the active theme from the repository
wp theme install $(wp theme list --status=active --field=name --path=/var/www/html) --force --path=/var/www/html
```

For **custom themes** not available on the repository, inspect each PHP file:

```bash
# Scan the active theme for suspicious code
grep -rn "eval(\|base64_decode\|str_rot13\|gzinflate" /var/www/html/wp-content/themes/your-theme/ --include="*.php"
```

### Clean the Uploads Directory

The `uploads/` directory should contain **no PHP files**:

```bash
# Find all PHP files in uploads (all suspicious)
find /var/www/html/wp-content/uploads/ -name "*.php" -type f

# Delete all PHP files in uploads
find /var/www/html/wp-content/uploads/ -name "*.php" -type f -delete

# Also search for files with double extensions
find /var/www/html/wp-content/uploads/ -name "*.php.*" -o -name "*.phtml" -o -name "*.php5"
```

### Check Suspicious Directories

```bash
# Look for hidden or unusually named directories
find /var/www/html/wp-content/ -type d -name ".*"
find /var/www/html/wp-content/ -type d -name "cache" -o -name "tmp" -o -name "sessions"

# List contents of suspicious directories
ls -la /var/www/html/wp-content/mu-plugins/
```

The `mu-plugins/` directory is often used to hide backdoors because **must-use plugins** are loaded automatically without activation.

## Step 4: Clean the Database

Malware injects code into the WordPress database, primarily in the `wp_options`, `wp_posts` and `wp_postmeta` tables.

### Search for Injections in wp_options

Connect to phpMyAdmin or use the MySQL command line:

```sql
-- Search for injected scripts in options
SELECT option_name, LEFT(option_value, 200)
FROM wp_options
WHERE option_value LIKE '%<script%'
   OR option_value LIKE '%eval(%'
   OR option_value LIKE '%base64_decode%'
   OR option_value LIKE '%document.write%';

-- Search for unknown admin users
SELECT * FROM wp_users WHERE user_login NOT IN ('admin', 'your_login');

-- Check user roles
SELECT u.user_login, m.meta_value
FROM wp_users u
JOIN wp_usermeta m ON u.ID = m.user_id
WHERE m.meta_key = 'wp_capabilities'
AND m.meta_value LIKE '%administrator%';
```

### Search for Injections in Posts

```sql
-- Posts containing malicious JavaScript
SELECT ID, post_title, LEFT(post_content, 200)
FROM wp_posts
WHERE post_content LIKE '%<script%'
   OR post_content LIKE '%<iframe%'
   OR post_content LIKE '%eval(%'
   OR post_content LIKE '%document.createElement%';

-- Search in post metadata
SELECT post_id, meta_key, LEFT(meta_value, 200)
FROM wp_postmeta
WHERE meta_value LIKE '%<script%'
   OR meta_value LIKE '%base64_decode%';
```

### Remove Fraudulent Admin Accounts

```sql
-- Identify suspicious accounts
SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM wp_users u
JOIN wp_usermeta m ON u.ID = m.user_id
WHERE m.meta_key = 'wp_capabilities'
AND m.meta_value LIKE '%administrator%'
ORDER BY u.user_registered DESC;

-- Delete a fraudulent user (replace the ID)
DELETE FROM wp_usermeta WHERE user_id = SUSPECT_ID;
DELETE FROM wp_users WHERE ID = SUSPECT_ID;
```

### Clean Malicious Cron Jobs

Attackers often schedule **cron jobs** to re-infect the site:

```sql
-- Display cron jobs
SELECT option_value FROM wp_options WHERE option_name = 'cron';
```

With WP-CLI:

```bash
# List all cron events
wp cron event list --path=/var/www/html

# Delete a suspicious cron event
wp cron event delete event_name --path=/var/www/html
```

### Clean Malicious Transients

```sql
-- Search for suspicious transients
SELECT option_name, LEFT(option_value, 200)
FROM wp_options
WHERE option_name LIKE '%_transient_%'
AND (option_value LIKE '%<script%'
     OR option_value LIKE '%eval(%'
     OR LENGTH(option_value) > 50000);

-- Delete all expired transients
DELETE FROM wp_options
WHERE option_name LIKE '%_transient_%';
```

## Step 5: Remove Backdoors

Backdoors are hidden access points that allow the attacker to **return even after cleanup**. They are the primary reason why sites get re-infected.

### Common Backdoor Signatures

```bash
# Backdoors using callback functions
grep -rl "preg_replace.*\/e\|create_function\|call_user_func\|array_map.*base64" /var/www/html/wp-content/ --include="*.php"

# Backdoors using global variables
grep -rl "\$_GET\[.*\]\|\$_POST\[.*\]\|\$_REQUEST\[.*\]" /var/www/html/wp-content/ --include="*.php" | head -20

# Backdoors using websockets or reverse connections
grep -rl "fsockopen\|socket_create\|stream_socket_client" /var/www/html/wp-content/ --include="*.php"

# Obfuscated code with character concatenations
grep -rl "chr(.*)\.\s*chr(\|\\\\x[0-9a-f]\{2\}" /var/www/html/wp-content/ --include="*.php"
```

### Check Server Configuration Files

```bash
# Search for .htaccess files in subdirectories
find /var/www/html/ -name ".htaccess" -type f

# Search for malicious php.ini files
find /var/www/html/ -name "php.ini" -o -name ".user.ini" | xargs cat

# Check auto_prepend files
grep -r "auto_prepend_file\|auto_append_file" /var/www/html/ --include=".htaccess" --include="php.ini" --include=".user.ini"
```

### Search for Webshells

```bash
# Classic webshells
grep -rl "FilesMan\|WSO\|c99shell\|r57shell\|b374k" /var/www/html/ --include="*.php"

# PHP files with deceptive names in wp-includes
find /var/www/html/wp-includes/ -name "*.php" | sort > /PATH/TO/current_includes.txt
find /PATH/TO/CLEAN-WP/wp-includes/ -name "*.php" | sort > /PATH/TO/clean_includes.txt
diff /PATH/TO/current_includes.txt /PATH/TO/clean_includes.txt
```

## Step 6: Verification with WP-CLI

**WP-CLI** has built-in commands to verify WordPress file integrity.

### Checksum Verification

```bash
# Verify core file integrity
wp core verify-checksums --path=/var/www/html

# Verify plugin integrity
wp plugin verify-checksums --all --path=/var/www/html

# List inactive plugins (potentially malicious)
wp plugin list --status=inactive --path=/var/www/html
```

### Update WordPress and Extensions

After cleanup, update everything to the latest versions. A [WordPress maintenance contract](/services/wordpress/maintenance) ensures these updates are performed regularly to prevent reinfection:

```bash
# Update WordPress core
wp core update --path=/var/www/html

# Update all plugins
wp plugin update --all --path=/var/www/html

# Update all themes
wp theme update --all --path=/var/www/html

# Remove unused themes and plugins
wp plugin delete $(wp plugin list --status=inactive --field=name --path=/var/www/html) --path=/var/www/html
wp theme delete $(wp theme list --status=inactive --field=name --path=/var/www/html) --path=/var/www/html
```

### Reset Passwords

```bash
# Change password for all administrators
wp user list --role=administrator --field=user_login --path=/var/www/html | while read user; do
  wp user update "$user" --user_pass="$(openssl rand -base64 24)" --path=/var/www/html
  echo "Password changed for: $user"
done

# Regenerate security keys
wp config shuffle-salts --path=/var/www/html
```

## Step 7: Final Verification and Checklist

Before bringing the site back online, perform a **complete verification**:

### Verification Checklist

| Check | Command | Status |
|-------|---------|--------|
| Core checksums | `wp core verify-checksums` | To verify |
| Plugin checksums | `wp plugin verify-checksums --all` | To verify |
| PHP files in uploads | `find uploads/ -name "*.php"` | Must be empty |
| Unknown admin accounts | SQL query on wp_users | To verify |
| Suspicious cron jobs | `wp cron event list` | To verify |
| Clean .htaccess | Manual inspection | To verify |
| Clean wp-config.php | Manual inspection | To verify |
| Security keys regenerated | `wp config shuffle-salts` | To verify |
| Passwords changed | All administrators | To verify |

### Scan with an External Tool

After manual cleanup, validate with an external scan:

```bash
# Scan with ClamAV if available
clamscan -r /var/www/html/ --infected --remove=no

# Use WPScan for additional verification
wpscan --url https://your-site.com --enumerate vp,vt
```

### Remove Maintenance Mode

```bash
# Delete the maintenance file
rm /var/www/html/.maintenance

# Flush the cache
wp cache flush --path=/var/www/html
```

## Post-Cleanup Actions

The cleanup is complete, but your work does not stop here. Several actions are necessary to **prevent re-infection**.

### Request a Google Review

If your site was flagged as dangerous by Google:

1. Log in to **Google Search Console**
2. Navigate to **Security & Manual Actions**
3. Click **Request a review**
4. Describe the cleanup actions taken

### Monitor the Site

```bash
# Create a basic monitoring script
cat > /usr/local/bin/wp-monitor.sh << 'SCRIPT'
#!/bin/bash
SITE_PATH="/var/www/html"
LOG="/var/log/wp-monitor.log"

# Verify checksums
wp core verify-checksums --path=$SITE_PATH >> $LOG 2>&1

# Check files modified in the last 24h
find $SITE_PATH -name "*.php" -mtime -1 >> $LOG 2>&1

# Check for PHP files in uploads
find $SITE_PATH/wp-content/uploads/ -name "*.php" >> $LOG 2>&1

echo "--- Scan completed on $(date) ---" >> $LOG
SCRIPT

chmod +x /usr/local/bin/wp-monitor.sh

# Add to crontab (daily scan at 3 AM)
echo "0 3 * * * /usr/local/bin/wp-monitor.sh" | crontab -
```

## When to Call an Expert

Manual cleanup is effective for standard infections, but some situations go beyond the scope of a self-guided intervention:

- **Persistent infections** that return despite multiple cleanups
- **Server-level compromise** (rootkit, unauthorized SSH access)
- **E-commerce sites** where sensitive data may have been exposed
- **Polymorphic malware** that changes signature with every scan
- **Blacklisting** by Google or browsers that persists after cleanup

Our team handles complex infections with an **in-depth cleanup protocol** and a no-reinfection guarantee. Discover our [WordPress malware removal service](/services/wordpress/malware-removal) or explore our [WordPress security solutions](/services/wordpress/security) for proactive protection.

For further reading, check our guide on [signs your WordPress site has been hacked](/blog/seo/wordpress-site-hacked-signs) and our catalog of [common WordPress malware in 2026](/blog/seo/common-wordpress-malware-2026).
