How to remove malware from a hacked WordPress site
Take the site offline, keep a copy of it as it is, and change every password and the secret keys. Then restore a backup from before the break-in, or replace WordPress and every plugin with clean copies and check what is left. Finish by finding and closing the way in.
- By
- WP Ministry
- Updated
In short
- Lock the site down first. Take it offline, keep a copy of the hacked state, and change every password and the secret keys.
- A backup from before the break-in is usually a surer route than cleaning by hand.
- Replace WordPress, plugins and themes with fresh copies. Do not repair infected files one at a time.
- Checksums cover WordPress and the plugins from WordPress.org. They say nothing about uploads, custom code or the database.
- The site is not clean until you know how they got in and have closed it.
- If the infection comes back, a backdoor was missed. That is the point to get help.
A hacked site pushes you to do something at once. WordPress's own advice starts in the opposite place: stay calm, and write down what you are seeing. The work has an order. Lock the site down, clean it, then find the way in. A site that is cleaned while the way in stays open can be broken into again the same way.
If you have a backup from before the break-in, read "Restore from a clean backup instead" further down before you clean anything by hand.
Confirm that the site was hacked
These are the signs WordPress's documentation treats as clear:
- A search engine or a browser shows a warning in place of your site.
- Your host has disabled the site, or tells you it is sending spam or attacking other sites.
- Visitors say their antivirus flags your pages.
- Something happened that nobody on your side did, such as a new user appearing.
- You can see it: pages show content you did not put there, or send visitors somewhere else.
Not every broken site is a hacked one. A blank page straight after an update points to a plugin conflict, not to a break-in.
Write down what you see, when you first saw it, and what changed on the site recently: a new plugin, an edit to the theme, a new user. Then ask your host. They may be able to confirm whether it is a hack at all, and whether other sites on the same account are affected.
A scanner shows more of the extent. One that reads the site from outside sees what visitors see, and a scanner plugin reads the files on the server. Neither finds everything. WordPress security plugins compared covers the plugin kind.
Lock the site down
Step 1: Take the site offline
Ask your host to take the site offline for visitors, or to limit it to your own IP address, so that nobody is served infected pages while you work. WordPress's own maintenance mode is no use for this: it ends by itself after ten minutes. A maintenance page from a plugin keeps visitors off your pages, but it does not stop a file the attacker planted from being opened directly.
Step 2: Keep a copy of the site as it is
Before you change anything, download every file and export the database. The copy is your record of what was done to the site, and it is what you go back to if the cleanup removes something you needed. Label it as infected, keep it off the server, and never restore it over a working site. Ask your host for the access logs now as well, before the older ones are gone. With WP-CLI, this command exports the database to a file in your home folder. It goes there, and not into the site's folder, because a file in the site's folder can be downloaded by anyone who guesses its name. Download the file and delete it from the server.
bashwp db export ~/hacked-state.sqlStep 3: Change every password
Change the hosting account, SFTP and SSH, the database, and every WordPress user who can log in to the dashboard, administrators first. Do it from a computer you have scanned for malware, because a password stolen from your own machine will be stolen again. After changing the database password at your host, put the new one in
wp-config.phpasDB_PASSWORD, or the site will show an error establishing a database connection.Step 4: Replace the secret keys
Replacing the secret keys in
wp-config.phplogs out everyone who is logged in, the attacker included. Without WP-CLI, WordPress.org runs a key generator that returns a fresh set of eight lines, and WordPress's FAQ for hacked sites links to it. Paste the new lines over the eight matching ones inwp-config.php.bashwp config shuffle-salts
Replace everything that has a clean original
Do not try to repair infected files. Anything that has an original gets deleted and put back from the original. Delete first, because copying fresh files over the top replaces the files that were changed and leaves the files that were added.
Step 1: Check WordPress against its checksums
The first command prints the version you are running. Write it down. The second compares every WordPress file with the checksums WordPress.org holds for that version, and warns about anything in the site's main folder that is not part of WordPress. Some of those files will be yours. Open each one you do not recognize.
bashwp core version wp core verify-checksums --include-rootStep 2: Replace WordPress with clean files
Delete the
wp-adminandwp-includesfolders. Nothing of yours is in them. Then download the same version again, using the number you wrote down in place of 1.2.3, and run the check from the first step once more.--skip-contentleaves out the default themes and plugins, sowp-contentis not touched, andwp-config.phpis not part of the download. Without WP-CLI, download the same version from WordPress.org, unzip it, and uploadwp-admin,wp-includesand the loose files beside them over SFTP.bashwp core download --version=1.2.3 --force --skip-contentStep 3: Check and reinstall every plugin
The command below compares each plugin with WordPress.org's checksums. A plugin bought elsewhere cannot be checked this way. What fails tells you where the attacker has been.
After it has run, reinstall all of them, whatever the result. List them with
wp plugin list --skip-plugins --skip-themes, delete each plugin's folder, and install a fresh copy from where it came from. For a plugin from WordPress.org that iswp plugin install plugin-folder-name --skip-plugins --skip-themes. A paid plugin comes from its author's own download page. Leave out any plugin you do not use or do not recognize. Look inwp-content/mu-pluginstoo: if neither you nor your host put a file there, treat it as suspect.bashwp plugin verify-checksums --all --skip-plugins --skip-themesStep 4: Do the same for themes
Delete the themes you do not use and reinstall the active one from its source. For a custom theme or a child theme, the only clean original is your own copy: the developer's repository or the files you were first given. Without one, read every file. Injected code tends to sit where it runs on every page, such as
functions.php,header.phpandfooter.php.Step 5: Look for PHP files in uploads
WordPress keeps your media in
wp-content/uploads, and the web server can write to that folder, which makes it a convenient place to hide a file. Run this from the site's main folder and open each file it lists. Some plugins keep small files of their own there. A file that no plugin of yours accounts for, or one filled with long unreadable strings, should come off the server.bashfind wp-content/uploads -type f -name "*.php"Step 6: Check the administrator accounts
List the administrators and delete any you did not create.
wp user delete 123 --reassign=1 --skip-plugins --skip-themesremoves user 123 and gives their posts to user 1.bashwp user list --role=administrator --skip-plugins --skip-themesStep 7: Read .htaccess and wp-config.php
Compare
.htaccesswith the standard block WordPress writes, which is shown in how to fix the 403 Forbidden error. Rules that send visitors elsewhere, and rules that neither you nor a plugin added, come out. Look for other.htaccessfiles in subfolders too. Comparewp-config.phpwithwp-config-sample.phpfrom a fresh download. Yours should differ only in the database details, the keys, the table prefix and settings that you, your host or a plugin added. Trace any line that loads another file before you trust it.
Restore from a clean backup instead
If you have a backup from before the break-in, restoring it is usually the better route. A restore puts back every file and the database as they were, so you do not have to find each thing the attacker changed. It replaces the file-by-file work above. Locking the site down and finding the way in still apply.
- The backup has to be older than the break-in, not older than the day you noticed. The first sign can come well after the first change. If you are unsure, go further back, and run the checksum commands above on what you restored.
- Restore into an empty folder. Delete the site's files first. You have the copy. Restoring over the top leaves planted files where they are.
- Whatever was added since the backup is gone. That means orders, comments, form entries and new posts. Content can be copied across by hand from the hacked copy. Files should not be, apart from media you have looked at.
- The hole is restored too. The backup holds the same out-of-date plugin, the old passwords and the old keys. Straight after restoring, change the passwords and the keys again and update everything.
Find how they got in
Look for the way in before the site goes back online.
- Out-of-date software. Once a fix for a hole is released, the hole is public, which leaves old versions more open to attack. Look in each plugin's changelog for a security fix you had not installed, then update everything. How to safely update WordPress has the order.
- A stolen or guessed password. Malware on your own computer can capture what you type. Scan every computer used to manage the site, and turn on two-factor authentication for every administrator.
- A plugin or theme from an untrusted source. A copy of a paid plugin that did not come from its author is not to be trusted. Remove it and get the plugin from the author.
- Another site on the same hosting account. A cleaned site beside an infected one may not stay clean. Every site on the account needs the same work.
- The access logs. Around the time of the first change, look for requests to files that should not exist.
If you cannot find it, the way in may still be open. Watch the site and scan it again over the following weeks.
When the site is clean, change every password and the secret keys once more, because the first change was made while the attacker may still have had access. Then put the site back online. The WordPress security guide covers what to tighten next.
Ask search engines to review the site
If Google has flagged the site, visitors see a warning in search results or a full-page warning in the browser. Cleaning the site does not remove it. You have to ask for a review.
In Google Search Console, open the Security issues report. If the site is not in Search Console yet, add it first. The report lists what Google found, with sample addresses. Fix the problem across the whole site, not only on the samples, then select "Request Review" and describe what you fixed. Google decides whether to lift the warning, and when.
Bing and the antivirus companies keep their own lists, each with its own request form. If your host suspended the account, ask which files they flagged, and when the site is clean send them the same description.
What a cleanup you do yourself can miss
The checks above show that WordPress and the plugins from WordPress.org match their originals. They show nothing about:
- the database, where injected scripts and spam links can sit inside posts and settings;
- a custom theme or plugin, when you have no original to compare it with;
- paid plugins, which WordPress.org holds no checksums for;
- the uploads folder, beyond the PHP files you looked for;
- anything on the hosting account outside WordPress, including other sites.
A backdoor can be one file, or a few lines inside a real one. Miss it and the attacker comes back without needing the original hole.
Stop and get help when:
- the infection returns after you have cleaned the site;
- you find code you cannot read and have no clean original to replace it with;
- you have no backup from before the break-in and the site runs custom code;
- the site holds customer accounts, orders or payment details;
- you cannot find how they got in.
Our malware removal service is a one-time clean-up with hardening, a blacklist removal request and a written report, and if the infection comes back within 30 days we clean the site again. Scanning, updates and backups from then on are what a care plan does, described under WordPress security.
Common questions
Should I delete everything and start again?
Starting again still needs your content, and the content lives in the database, which is one of the places an infection can sit. A fresh install with the old database imported is not clean merely because its files are new. The practical form of starting again is restoring a backup from before the break-in. Without one, replace every file that has an original and have the database checked.
A scanner says the site is clean. Is it?
It means that scanner found nothing it recognizes. WordPress's own advice is that no single scanner is the best approach and that using more than one improves the odds. Treat a clean scan as one piece of evidence, beside the checksums and a quiet few weeks.
Do I need to tell my customers?
If the site holds accounts, orders or form entries, assume the attacker could read them. Anyone who could change your files could also read the database password in wp-config.php. Reset your users' passwords. Whether you must notify people or a regulator depends on the law where you and your customers are, so ask someone qualified, and ask early.

