# How to clean a hacked WordPress site, step by step

> How to clean a hacked WordPress site: isolate it, find backdoors, clean files and database with wp-cli, restore a clean backup, then harden it.

Source: https://zopanel.net/blog/wordpress-malware-cleanup  
Updated: 2026-10-21

## Key takeaways

- Before you clean a hacked WordPress site, isolate it and back up the infected state.
- If you have a backup from before the infection, restoring it and patching the hole is usually faster and safer than cleaning file by file.
- Replace WordPress core, plugins and themes with official copies, then check uploads, mu-plugins, wp-config.php, .htaccess and the database.
- Change every password and security key once the site is clean, and find and fix the way the attacker got in.

To **clean a hacked WordPress site**, work in order: isolate the site, back up the infected state as it is, change passwords, then choose one of two paths. If you have a clean backup from before the hack, restore it and patch the vulnerability. If not, replace WordPress core, plugins and themes with official copies, then clean the places malware likes to hide: the uploads folder, mu-plugins, `wp-config.php`, `.htaccess` and the database. Finally, change passwords again, update everything and find how the attacker got in, or the site will be reinfected within days.

This guide covers the warning signs, each step with concrete commands (for Ubuntu 24.04 and Debian 12), how to do it in ZoPanel, and how to harden the site afterwards. If the site holds customer or payment data, consider hiring a professional; this guide helps you handle the common cases yourself and know what to ask.

## Signs your WordPress site has been hacked

The [official WordPress documentation](https://wordpress.org/documentation/article/faq-my-site-was-hacked/) lists the clear indicators, and in practice we see a few more:

- Google shows "This site may be hacked" or a red warning page.
- Visitors from Google (especially on mobile) are redirected to gambling, pharma or scam pages, while you see nothing when you visit directly.
- Unknown administrator accounts appear under Users.
- A `site:yourdomain.com` search shows thousands of strange pages, often Japanese or designer-goods spam.
- Your host suspends the site or reports that the server is sending spam.
- CPU spikes, and unfamiliar PHP files appear in `wp-content/uploads`.
- Visitors' antivirus software flags the site.

Modern malware often **hides from administrators**: it shows only to visitors from Google, only on mobile, or once per IP. A site that looks normal to you is not necessarily clean.

## Step 1: Stay calm, document and isolate

Write down what you see, when, and what changed recently (new plugins, updates, new users with access). These notes help when you look for the cause.

Then isolate:

- Turn on maintenance mode, or temporarily put the whole site (`/`) behind a password-protected directory.
- If the site is sending spam, stop that account's outgoing mail for now.
- On shared hosting, tell your provider: the infection may spread to other sites in the same account.

## Step 2: Back up the infected state

It sounds backwards, but save a full copy (files and database) **before you change anything**. If cleaning breaks the site you have a reference; if you need to investigate, it is your evidence. Name it clearly, for example `infected-2026-11-11.tar.gz`, and never restore it by mistake.

```bash
cd /home/client1/domains/example.com
tar czf ~/infected-$(date +%F).tar.gz public_html
wp db export ~/infected-$(date +%F).sql --path=public_html --skip-plugins --skip-themes
```

`--skip-plugins --skip-themes` matters: wp-cli loads WordPress to run commands, and without these flags you may be executing the malware itself.

## Step 3: Change passwords and end every session

Change the password for every access point: WordPress admins, SFTP/FTP, the control panel, the database, email. Generate new security keys (salts) in `wp-config.php` so every session, including the attacker's, is logged out:

```bash
wp config shuffle-salts --path=public_html --skip-plugins --skip-themes
```

List administrators and remove unknown ones (reassigning their posts to a legitimate user if needed):

```bash
wp user list --role=administrator --path=public_html --skip-plugins --skip-themes
```

You will change passwords once more after the site is clean, because a leftover backdoor could steal the new ones.

## Step 4: Restore a clean backup or clean manually?

| Situation | What to do |
|---|---|
| A backup from before the infection, and you can accept losing changes since | Restore, then update and patch immediately |
| A backup, but you are unsure when the infection started | Restore files from the older backup, keep the current database after cleaning it |
| No clean backup | Clean manually, as in Step 5 |
| An e-commerce site with payment data | Hire a professional, preserve evidence |

Malware often sits quietly for weeks before it shows, so "yesterday's backup" is not necessarily clean. Run the checks below after restoring too.

## Step 5: Clean WordPress piece by piece

### 5.1. Verify core against the official checksums

```bash
wp core verify-checksums --path=public_html
```

[wp core verify-checksums](https://developer.wordpress.org/cli/commands/core/verify-checksums/) compares core files with WordPress.org's published checksums without loading WordPress. Modified files and extra files in core folders are listed.

The safest approach is to replace core with a clean copy of **the same version** (keeping `wp-content` and `wp-config.php`):

```bash
VER=$(wp core version --path=public_html --skip-plugins --skip-themes)
wp core download --version="$VER" --skip-content --force --path=public_html
```

This overwrites files but does **not delete** files an attacker added to `wp-admin` or `wp-includes`. Delete those two folders first and then download, or remove the files verify-checksums reports as "should not exist".

### 5.2. Plugins and themes

```bash
wp plugin verify-checksums --all --path=public_html --skip-plugins --skip-themes
```

Plugins from WordPress.org are checked against checksums. Premium plugins and commercial themes have no public checksums: download the latest version from the developer and replace the whole folder. Delete every unused plugin and theme. "Nulled" (pirated) plugins are a very common infection source; remove them now.

### 5.3. Where malware hides

```bash
cd /home/client1/domains/example.com/public_html

# PHP files in uploads (WordPress never needs these)
find wp-content/uploads -type f -name "*.php"

# Must-use plugins: run automatically, not shown in the normal plugin list
ls -la wp-content/mu-plugins 2>/dev/null

# PHP files changed in the last 14 days
find . -type f -name "*.php" -mtime -14 -printf "%TY-%Tm-%Td %p\n" | sort

# Common obfuscation patterns
grep -rlE "eval\(base64_decode|gzinflate\(base64_decode|str_rot13\(" --include=*.php .
```

Also check `wp-config.php` (stray `include` lines at the top or bottom), `.htaccess` in every folder (redirects based on User-Agent or a Google Referer), and the theme's `index.php`, `header.php`, `footer.php` and `functions.php`. The `grep` can return false positives, since some legitimate plugins use `base64`; open a file before deleting it.

### 5.4. The database

Database malware is usually `<script>` tags injected into posts, widgets or theme options:

```bash
wp db search "<script" --all-tables --path=public_html --skip-plugins --skip-themes
wp db search "eval(" --all-tables --path=public_html --skip-plugins --skip-themes
```

Also check for unfamiliar scheduled events (`wp cron event list`) and the `siteurl` and `home` values in the options table. Fix them with Adminer or phpMyAdmin, and always back up the database before deleting rows.

## Step 6: Update, change passwords again, harden

1. Update WordPress core, every plugin and theme, and move to a supported PHP version.
2. Change every password again and regenerate the salts.
3. Turn on two-factor authentication for administrators.
4. Disable the dashboard file editor (`DISALLOW_FILE_EDIT`), block PHP execution in uploads, and make `wp-config.php` readable only by the account.
5. Find the cause: an unpatched plugin, a brute-forced weak password, or a local computer with a trojan stealing FTP credentials. Scan your own machine too.

If Google flagged the site, go to the Security Issues report in Google Search Console and request a review once the site is clean.

## How to do it in ZoPanel

These tools are built into the panel and shorten the steps above considerably. Details are in the [Security](/docs/security) and [WordPress Toolkit](/docs/wordpress) docs.

- **Malware scan:** looks for webshells, backdoors, obfuscated code, injected JavaScript and PHP files in upload folders. Every account is scanned automatically each Sunday, and customers can click **Scan now**. For each finding, **Quarantine** moves the file out of the website to `/var/lib/zopanel/quarantine/<user>/`, a folder only root can read, so it cannot run, or **Ignore** marks a false positive.
- **WordPress tab → Security:** **Verify file integrity** compares core and plugins with official checksums; **Rotate security keys** generates new salts and signs everyone out; **Reset password** works for any user; **Apply all** sets the recommended options (file editor off, PHP blocked in uploads, `xmlrpc.php` blocked, `wp-config.php` at mode 600).
- **Restore single files or folders** from nightly incremental snapshots in the **Snapshots (incremental)** card on the **Backups** page, and restore databases from database backups (the current data is backed up first). Click **Snapshot now** to save the infected state before you clean.
- **Web application firewall (ModSecurity with the OWASP Core Rule Set):** run **Detect only** for a few days, then switch to **On (block attacks)**.
- **Safe updates:** snapshot, update, health check and automatic rollback, so you can turn on nightly automatic updates without fear of breaking the site.

Just as important is what sits outside WordPress: each hosting account runs as its own Linux user with its own PHP pool, so an infected site cannot read other customers' files, and per-account hourly sending limits stop a hacked site from getting the server's IP blocklisted. We explain this architecture in [Why a hosting control panel should never run as root](/blog/secure-hosting-panel-architecture).

## Mistakes that lead to reinfection

- **Deleting only what the scanner flagged.** There is often a second backdoor, planted precisely to survive the first cleanup.
- **Changing passwords before the cleanup and never again.** A leftover backdoor can capture the new ones.
- **Reinstalling a pirated plugin.** That is often how the malware got in.
- **Restoring a backup without patching.** The attacker returns the same way within hours.
- **Skipping the database.** The files are clean, but injected scripts in posts still redirect visitors.
- **No off-server backups.** If the attacker controls the account, backups in the same account are not safe either.

## Conclusion

Cleaning a hacked WordPress site is a process: isolate, preserve evidence, change passwords, restore or replace with official copies, clean the hiding places, then fix the actual vulnerability. Prevention is still cheaper: regular updates, off-server backups, per-account isolation and scheduled scans.

ZoPanel includes malware scanning, WordPress integrity checks, safe updates with rollback and nightly snapshots, all on the Free plan. See [pricing](/pricing), or read our guide to [securing an Ubuntu VPS](/blog/secure-ubuntu-vps) to harden the whole server.

## Frequently asked questions

### How do I know if my WordPress site is hacked?

Common signs: Google warnings, visitors from Google redirected to strange sites, unknown administrators, thousands of odd pages in `site:` results, PHP files in the uploads folder, or your host reporting spam from the server. Run `wp core verify-checksums` and a malware scan to confirm.

### Can a security plugin clean the site for me?

Scanner plugins help with detection, but they run inside the same infected WordPress, where malware can interfere with them. Combine them with outside checks: wp-cli checksums, a server-level scan, and a manual look at uploads, mu-plugins and `.htaccess`.

### Is restoring a backup enough to remove malware?

Only if the backup predates the infection and you patch the vulnerability right after restoring. Malware can sit quietly for weeks, so verify the restored site with checksums and a scan before you reopen it.

### How long does Google take to remove a hacked-site warning?

Once the site is clean, request a review in the Security Issues report of Google Search Console. Timing varies case by case, and the request is rejected if Google still finds malware, so check thoroughly before submitting.

### Why does my site keep getting hacked after cleaning?

Usually a backdoor was missed, the original hole (an outdated plugin, a weak password) was not fixed, or the admin's computer has a trojan stealing passwords. Check all three before calling the cleanup done.
