
How to Clean a Hacked WordPress Site (Step by Step)
Your WordPress site is hacked and you want it clean. Not a marketing pitch, not a list of vague "steps" that ends in "contact us for a quote." An actual procedure you can follow. That's what this is — file scan, database pass, vulnerability close, verification, and an honest read on the point at which DIY stops being worth it. If you're not sure the site is actually hacked yet, the diagnostic walkthrough covers the warning signs first.
Before you touch anything
Three things in this exact order, before any cleanup work. Skip them and you'll lose evidence, lock yourself out, or accidentally re-deploy the malware after you've cleaned it.
First — take a full backup of the current state. Files and database. Yes, the infected state. You need this for two reasons: if your cleanup goes sideways you can roll back, and if you eventually need to hand the case to someone (host, professional, lawyer if data was leaked) they'll want to see what was on the server. Use your host's snapshot tool, or `wp db export` plus a tar of `wp-content/`. Store the backup off the server.
Second — change every password the site touches. WordPress admin accounts, FTP/SFTP, the hosting control panel, the database user, any API keys exposed in `wp-config.php` or in plugin settings. If the attacker had file-level access they may have read these. Treat them all as compromised.
Third — put the site in maintenance mode or take it offline. Any visitor hitting an infected page is being served malware. Most hosts have a one-click maintenance toggle; otherwise drop a `.maintenance` file in the WordPress root or use Cloudflare's 'Under Attack' mode. The goal is to stop the bleed while you work.
Realistic time on this stage: 20–40 minutes. It feels like overhead. Don't skip it.
Stage 1 — clean the files
WordPress files come in three buckets: core (the WordPress install itself), themes, and plugins. The cleanest, fastest approach is to nuke and replace the first two buckets entirely. Plugins get the same treatment, with one careful exception.
Replace WordPress core. Download a fresh copy of your exact WordPress version from wordpress.org. Delete `wp-admin/` and `wp-includes/` from your install. Upload the fresh copies. Replace every file in the WordPress root *except* `wp-config.php` and `.htaccess` — those have your settings. After replacing, open the new `.htaccess` and `wp-config.php` and compare them against the originals; attackers love hiding payloads in both.
Replace your active theme. If you use a stock theme (Astra, GeneratePress, Twenty Twenty-Four), download a fresh copy from the theme's source and replace the entire theme folder. If you're on a custom or modified theme — and you don't have a clean copy in version control — you'll need to manually scan it. Look for any PHP file with base64-encoded content, `eval(`, `gzinflate(`, `assert(`, or any file whose modified date is recent (within the infection window) and that you didn't change.
Replace plugins. Same rule: re-download every plugin from wordpress.org or the original vendor and replace its folder entirely. Don't just "update" — a malicious payload added to a legitimate plugin survives a normal update. Delete plugins you no longer use, period. The exception: any premium plugin where you only have the original install file, no live download URL — for those, scan the existing folder using the same indicators as the theme above. The backdoor-detection walkthrough lists the exact patterns to grep for.
Don't forget `wp-content/uploads/`. The uploads directory should contain images, PDFs, videos — never PHP. Run `find wp-content/uploads -name '*.php'` from SSH or your file manager. Anything it returns is malicious. Delete it. While you're there, check for files with names like `wp-1234.php`, `index2.php`, `lock360.php` — random or near-WordPress-lookalike names are the giveaway.
Realistic time on this stage: best case 1 hour (small site, stock theme, few plugins), typical 2–4 hours, worst case 8+ hours (custom theme without a clean copy, dozens of premium plugins). If you're past hour 4 and finding more, that's the signal to consider stopping and using the Toolkit or the Fix service — DIY cost has crossed the threshold.
Stage 2 — clean the database
File cleanup gets you maybe 70% of the way. Modern WordPress malware also embeds itself in the database — most often as injected content in posts, fake admin users, or malicious entries in `wp_options`. A scan of the file system won't catch any of it.
Open phpMyAdmin or use WP-CLI. Three sweeps in order:
Sweep 1 — admin users. `SELECT user_login, user_email, user_registered FROM wp_users;` Anything you don't recognise, especially recent registrations with admin role, is a planted account. Delete the user *and* their meta entries (`DELETE FROM wp_usermeta WHERE user_id = X;`). Don't just demote — attackers re-promote.
Sweep 2 — `wp_options` tampering. Two suspect rows specifically: `siteurl` and `home`. Verify they both contain your actual domain, not a redirect target like `evil.example.com`. Then check for unfamiliar option_name entries with serialized data containing URLs you don't recognise — common malware persists itself here. The database malware detection guide lists the exact queries.
Sweep 3 — content injection. `SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%' OR post_content LIKE '%base64%' OR post_content LIKE '%eval(%';` Inspect every result. Spam keywords (Viagra, casino, payday loans) embedded in post content are the obvious case; obfuscated `<script>` tags pointing to external domains are the harder-to-spot one. Edit each affected post to remove the injection.
If your site uses WooCommerce or a forms plugin, also check the order and submission tables for placeholder spam. Attackers sometimes use compromised stores as proxies.
Realistic time on this stage: 30–90 minutes for a normal-sized site. Multiplies fast on sites with thousands of posts.
Stage 3 — find how they got in
If you skip this stage you'll be doing the cleanup again in a fortnight. Three categories cover ~95% of WordPress entry points:
Category 1 — outdated plugins with known CVEs. Run a vulnerability scan against your current plugin list. The free GuardingWP scanner checks against the WPVulnerability.net database. Wordfence, Patchstack, and WPScan all do the same. Any plugin flagged with a known CVE that pre-dates your infection date is your most likely entry point. Update or remove it.
Category 2 — weak admin credentials, brute-forced. Check your access logs (your host should have these — Apache `access.log` or nginx equivalent) for the period before the infection. Filter for `POST` requests to `wp-login.php` or `xmlrpc.php`. A high volume from a single IP, or a slow drip from many IPs, is brute-force. The fix is 2FA + a strong password + (optionally) blocking XML-RPC if you don't actively use it.
Category 3 — exposed configuration or files the attacker read directly. If `wp-config.php` was world-readable, if directory listing was on so they could see your plugin paths, if `.git/` or backup files were exposed in the webroot — any of these gives an attacker an asymmetric advantage. Scan for these now and close them.
If none of the three categories explains it, your hosting account itself may have been compromised, or the attacker came in via a different site on the same server (cross-site contamination on shared hosting). Contact your host with the dates of the infection and ask them to review their server-side logs.
Stage 4 — verify before going live
Before you take the maintenance page down, run two passes to confirm the cleanup actually worked.
Pass 1 — re-scan. Run the GuardingWP scanner against your domain. If anything in the 11-check list still flags, address it before going live. Then run a second-opinion scan: Sucuri's free SiteCheck hits your site externally and looks for blocklist status, defacement strings, and visible injections.
Pass 2 — find what the cleanup missed. The post-cleanup verification walkthrough lists the specific commands to find dormant backdoors that didn't show up in the file pass — modified-time anomalies, hidden cron jobs, suspect uploads with PHP execution rights. Run them all, even the ones that feel paranoid.
If both passes are clean, take the site out of maintenance mode. Then submit the URL to Google Search Console for re-indexing if you were flagged.
Realistic time on this stage: 30 minutes if everything's clean. Hours if pass 2 turns up something — in which case loop back to Stage 1 with the new info.
Stage 5 — harden so this doesn't happen again
Cleaning the site without hardening it is mopping a flooded floor without turning off the tap. Five things, in priority order:
1. Update everything. WordPress core, every theme (active and inactive), every plugin (active and inactive). Delete anything you don't actively use — every dormant plugin is a future attack surface.
2. Strong unique admin password + 2FA. Use a password manager. Add two-factor auth via a maintained plugin. Pick one with active maintenance and recovery codes (the most common cause of a permanent lockout is no recovery codes saved).
3. Close the entry-point category that let them in. If it was an outdated plugin: subscribe to vulnerability alerts (GuardingWP Pro emails you when a watched plugin gets a fresh CVE). If it was brute-force: limit login attempts and consider moving the login URL. If it was an exposed file: lock down `wp-config.php`, `.htaccess`, and disable directory listing.
4. Run the standard hardening checklist — it covers the 12 most common vulnerability classes the GuardingWP scanner detects, with fixes for each.
5. Set up monitoring. Weekly automated scans catch new vulnerabilities as they're disclosed against your installed plugins, before attackers find them on your site. That window — between a CVE being published and mass-exploitation — is the entire point of monitoring. GuardingWP Pro does this; so do the comparable monitoring tiers from Wordfence and Sucuri.
When DIY is the wrong call
Five honest signals that DIY cleanup has stopped being worth it:
1. You're past 6 hours and finding more. Each new infected file you discover means the next pass might also miss something. Time-on-task multiplies; risk doesn't reduce.
2. You don't have a clean copy of your custom theme or premium plugins. Without a known-good baseline, you're scanning code by hand, which is the failure mode.
3. The site collects payment data or sensitive personal info. PCI-DSS and GDPR raise the cost of a missed backdoor by an order of magnitude. Don't gamble on a self-cleanup if a leak would be a regulated incident.
4. You've cleaned it once already and it came back. Re-infection means the previous cleanup missed the persistence mechanism. The second cleanup is harder than the first.
5. You don't run a backup process. Without backups, every step you take is irreversible. Professional services bring their own snapshot systems to roll back from.
If any of those apply, two paths. The Forensic Toolkit walks you through the file-level scans and database queries that uncover the dormant artifacts you'd otherwise miss — DIY but with the tooling. The Fix service is hands-off: send the credentials, receive a clean site. Both end at the same place; the choice is how much of your time the cleanup is worth.
Related fix guides
Vulnerable Plugins Detected
One or more WordPress plugins has known security vulnerabilities. Learn how to find and update them.
XML-RPC Enabled
XML-RPC lets attackers run thousands of login attempts at once. Learn how to disable it in two steps.
Login Page Exposed
Your WordPress login page is publicly accessible. Learn how to protect it from brute-force attacks.
Directory Listing Enabled
Anyone can browse your WordPress uploads folder. One line in .htaccess closes this immediately.
GuardingWP checks your site for the 11 most common WordPress vulnerabilities — plus scans your installed plugins against the known CVE database. Free, no account required.
Scan to find which entry point let them in →DIY past hour 4?
The Forensic Toolkit gives you the same file-by-file scans and database queries this walkthrough describes — pre-built, with the patterns updated for current malware families. One-time $25.
Get the Forensic Toolkit — from $25 →Prefer to have this handled for you? Get this fixed — Full Hardening ($149) →