WordPress Malware Removal: The Procedure We Run On Client Sites

WordPress Malware Removal: editorial feature spread showing the procedure we run on client sites. Large Fraunces serif title with Malware set in burgundy italic, pull quote, and masthead metadata.

This guide covers the procedure we run on client sites at WP Spear. It assumes you can reach SSH or SFTP on the infected site. If you cannot, skip to when to pay a pro.

A WordPress malware cleanup looks simple in articles and complicated in practice. The part that goes wrong is never the cleanup itself. It is the reinfection that arrives 48 hours later because the entry point was never closed. This is how we avoid that.

Before you touch anything (5 minutes)

The first thing attackers do after planting a backdoor is phone home. Every minute the site stays reachable, the payload is fetching instructions, adding new admin users, or rewriting your .htaccess. So the first thing you do is cut it off.

  1. Take the site offline. Add a maintenance-mode .maintenance file at the WordPress root, or return a 503 from nginx. Do not use a plugin for this, the plugin runs inside the compromised install.
  2. Block outbound connections at the host firewall if you can. This stops backdoors from receiving commands while you work.
  3. Freeze credentials. Reset the hosting control-panel password and the SFTP password. Do not yet change WordPress admin passwords, we will do that after cleanup.

If the site sits on shared hosting and you cannot do any of the above, open a ticket with the host and ask them to suspend it. Most providers will do this within the hour.

Identify what you are dealing with (15 minutes)

Before cleaning, figure out the malware family. The cleanup steps are mostly the same but knowing the family tells you where to look and what you will miss if you are careless.

Check for these signatures:

  • Pharma hack. Pharmaceutical keywords (viagra, cialis, xanax) appear in Google results for the site. The injection is usually invisible when you view the homepage as an admin, but visible when Googlebot fetches it. Use Sucuri SiteCheck or visit the site with a Googlebot user-agent to confirm.
  • Japanese keyword hack. Japanese characters in page titles, URLs, or meta descriptions in Google’s index. Often paired with an unauthorised Search Console property owner, check Google Search Console users immediately.
  • SocGholish / fake browser update. Visitors see a “your browser is out of date” overlay that downloads a .zip. Sucuri recorded over 143,000 SocGholish infections in their 2023 threat report, and the campaign is still active in 2026.
  • Redirect malware (Balada Injector and variants). Visitors land on the site and get bounced to scam pages. Usually the injection sits in header.php, database options, or a must-use plugin.
  • Backdoor only. No visible symptom. Attackers reserving access for later. These are the hardest to find and the easiest to miss.

Also check the obvious places most guides skip:

  • wp-admin users. Run wp user list --role=administrator. Any account you do not recognise is a compromise.
  • Must-use plugins. Sucuri flagged mu-plugins abuse in March 2025. Files in wp-content/mu-plugins/ load automatically and never appear in the plugins admin screen. Check this folder before anything else.
  • Scheduled tasks. wp cron event list. Attackers schedule cron events to reinstall themselves after cleanup.

Back up everything, including the infected version

Take a full filesystem and database backup before any change. You want a clean evidentiary copy in case the cleanup breaks something. The backup should live off the compromised server, pull it down with rsync or mysqldump over SSH.

rsync -av --exclude='wp-content/cache' user@host:/var/www/site/ ./infected-backup/
ssh user@host 'mysqldump --single-transaction wp_db' > ./infected-backup.sql

Do not overwrite any earlier clean backup with this one. Name it infected-YYYYMMDD so you cannot confuse it.

Find the malicious code

This is the part that takes actual time. Budget 30 to 60 minutes depending on site size.

Compare against a clean WordPress core

Download the exact version of WordPress that is running on the site from wordpress.org/download/releases, then diff the two installations. Everything in /wp-admin/ and /wp-includes/ should be byte-identical to the core release. Anything different is suspect.

# From the site root, compare against a freshly extracted clean copy:
diff -qr ./wp-admin/ /path/to/clean/wp-admin/
diff -qr ./wp-includes/ /path/to/clean/wp-includes/

Every guide mentions this step. Almost nobody does it. Do it.

Find recently modified PHP files

find . -type f -name '*.php' -mtime -14

Any PHP file modified in the last two weeks that you did not modify yourself is suspect. Prioritise anything under wp-content/uploads/, wp-content/mu-plugins/, the plugin and theme root directories, and wp-config.php.

Search for common malicious patterns

find . -type f -name '*.php' | xargs egrep -l "eval\s*\(|base64_decode|gzinflate|str_rot13|assert\s*\(\\$_"

These functions are not always malicious (plugins use base64_decode for legitimate reasons) but the combination eval(base64_decode(...)) is a near-certain backdoor. Open every match and read it.

Check the database

Malicious code also lives in the database, usually in wp_options, wp_posts (post_content), and wp_usermeta.

wp db query "SELECT option_name, LENGTH(option_value) FROM wp_options ORDER BY LENGTH(option_value) DESC LIMIT 20;"

Options with abnormally long values are usually injections. Common names: class-wp-form-controller, widget_block, random alphanumeric strings. Check each one.

For injected links in post content:

wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%' OR post_content LIKE '%display:none%';"

Clean it

Do not patch individual files. The clean approach is:

  1. Replace all WordPress core files with the fresh download you used for diffing. Overwrite wp-admin/ and wp-includes/ entirely.
  2. For every plugin you are keeping, delete it and reinstall from wordpress.org/plugins. Do not restore plugins from the infected backup, they might be carrying the injection.
  3. For the active theme, if it is a stock theme, reinstall from wordpress.org. If it is a custom theme or a paid theme, you need to diff it against the vendor’s latest release. No shortcut here.
  4. Empty wp-content/uploads/ of any PHP files. Uploads should never contain executable code.
    find wp-content/uploads -type f -name "*.php" -delete
  5. Empty wp-content/mu-plugins/ unless you legitimately use it. Most sites do not.
  6. Clean the database. Remove injected options, rewrite post_content where injections were found, delete unauthorised users and their usermeta rows.

After this, your WordPress install should match the vendor distributions exactly, plus legitimate plugins, plus your actual content.

Close the door

The cleanup is worth nothing if the entry point stays open. This is where most DIY attempts fail. Sucuri’s threat report found that close to 40% of infected CMS sites were running outdated software at the time of infection. If you reinstalled plugins but did not rotate credentials, did not patch the vulnerable plugin, and did not audit user accounts, you will be hacked again. See the hardening desk for the procedures we run after every cleanup.

Required steps:

  • Rotate every credential. WordPress admin users, database password, hosting control panel, SFTP, any API keys in wp-config.php. Regenerate the WordPress secret keys.
  • Delete every administrator account you do not personally know, then rotate passwords on the ones you keep.
  • Update WordPress core, plugins, and themes to the current versions. Check the WPScan vulnerability database for the specific plugins on the site, and remove anything flagged with no fix available.
  • Fix file permissions: find . -type d -exec chmod 755 {} \; and find . -type f -exec chmod 644 {} \;, with wp-config.php at 600.
  • Disable file editing from the admin: add define('DISALLOW_FILE_EDIT', true); to wp-config.php.
  • Disable PHP execution in uploads. Drop an .htaccess into wp-content/uploads/ with <Files *.php> Require all denied </Files> (Apache) or a location block (nginx).

Verify from the outside

Do not trust what you see when logged in. Attackers regularly cloak injections from logged-in admins. Three checks:

  1. Fetch the site as Googlebot. curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://yoursite.com/ | grep -i "viagra\|cialis\|xanax\|パチンコ". No matches means the cloaked injection is gone.
  2. Scan with external tools. Sucuri SiteCheck, VirusTotal, Google Transparency Report.
  3. Request blocklist removal. If Google flagged the site, go to Search Console, Security Issues, and request a review. If the site is on Google’s Safe Browsing list, reviews typically clear within 72 hours.

Watch for 48 hours

Backdoors that escaped cleanup usually reactivate within two days. Signs of reinfection:

  • New files appearing in wp-content/uploads/ or mu-plugins/.
  • New administrator users you did not create.
  • New scheduled cron events.
  • New outbound DNS queries to suspicious domains (monitor with tcpdump if you have host access).

Set up a simple file-integrity monitor. inotifywait on the WordPress directory works for a few days of paranoid monitoring, or install a scanner plugin if you must use one.

When to pay a pro

This guide covers the common case. It does not cover:

  • Sites with no backup and no SSH/SFTP access
  • Compromises involving server-level rootkits or the hosting account itself
  • Sites with proprietary custom themes that cannot be diffed against a vendor release
  • Cases where the site has been hacked three times already, which strongly suggests the hosting neighbour or the admin’s device is the actual source
  • Sites with regulatory exposure (HIPAA, PCI-DSS) where evidence preservation matters

For those, hire Sucuri, MalCare emergency cleanup, or a comparable firm. Expect $199 to $499 for a standard cleanup, more for complex cases. Our plugin reviews cover the automated tools and their limits, and the incident desk publishes the timelines of cleanups we’ve run.

What most cleanup guides get wrong

Reading twenty competing guides, three patterns repeat:

  • They over-index on scanner plugins. Wordfence and MalCare are useful, but they miss malware in mu-plugins, database options, and cloaked SEO injections. A scanner plugin is part of the answer, not the answer.
  • They omit credential rotation. If you cleaned the files but kept the old admin password, you have not closed the door.
  • They stop at “now install a security plugin.” The plugin does not fix the vulnerable version that let the attacker in.

WordPress dominates infections not because the platform is unsafe, but because the long tail of plugins and themes is where bugs live. Sucuri’s 2024 research pegged WordPress at the overwhelming majority of infected CMS sites they clean. That ratio is not a platform problem, it is a patching problem. Someone installed a plugin with a known WPScan-tracked CVE, never updated it, and never noticed. The cleanup is the symptom. The patching discipline is the cure. See our News desk for the disclosures, campaigns, and plugin-lifecycle events we track and the plugin reviews for tools that automate patching.

Frequently asked questions

How long does a WordPress malware cleanup take?

A clean job on a small site with a recent backup and SSH access takes about 90 minutes. Add an hour for sites with custom themes, another hour for database cleanup when the malware injected post_content. Cases with no backup or no server access routinely run four to six hours.

Can I skip backups and just clean in place?

No. You will break something and have no rollback. The backup is cheap insurance.

My WordPress site keeps getting hacked after cleanup. Why?

One of three reasons. You missed a backdoor (usually in mu-plugins/ or the database). You did not rotate credentials, so the attacker logged back in. Or the entry point is not your site at all: the hosting neighbour or your admin device is compromised.

Is a paid service worth it?

For small business sites without a technical owner, yes. $199 to $499 to not run the commands above yourself is reasonable. For agencies managing multiple sites, learn the procedure above and run it in-house.

Will Wordfence or MalCare find everything?

No. Automated scanners catch most common malware signatures but miss cloaked SEO injections, mu-plugin abuse, and custom backdoors. Use them, but do not assume the scan report is complete.

What do I do about Google blocklisting?

Open Google Search Console, go to Security Issues, fix all reported problems, and click “Request a review.” Clearance usually takes 24 to 72 hours. While you wait, add a disclaimer to your site explaining the situation.

Do I need to notify users?

If the site handled login credentials, payment details, or personal data, probably yes. GDPR requires breach notification to the relevant data protection authority within 72 hours of discovery. Consult your legal counsel if you are unsure.

Filed under
Written by
WP Spear
Editor · Incident desk
Written by
WP Spear Editor · Incident desk

WP Spear publishes WordPress security research, incident response, and hardening procedures from practitioners who actually run WordPress sites.

Filed APR 17, 2026 Last reviewed APR 25, 2026
Keep reading · the briefing
One email a week. Only the CVEs that matter.
Scroll to Top