This guide assumes you have confirmed the infection. If you have not, start with the five-minute check. If you have just found the problem, do the first-hour steps in How to Fix a Hacked WordPress Site first. They cover the backup, the offline decision and changing every credential, and you should not skip them.
public_html. Test them on a staging copy if you can, and put the live site in maintenance mode while you work.Step 1: Make a quarantine folder
Move suspicious files out of the site instead of deleting them. A false positive is easy to restore, and a deleted file is not. Put the folder outside the web root:
mkdir -p ~/quarantine
Step 2: Replace WordPress core
Core folders should never contain hand-edited code, so replacing them is safe. Save the version number first, because you are about to remove the file that stores it:
VERSION=$(wp core version)
rm -rf wp-admin wp-includes
wp core download --force --skip-content --version=$VERSION
wp core verify-checksums
The download restores clean copies of the core folders and the root files such as index.php and wp-login.php. The last command should report that everything verifies. Any "File should not exist" line is a stray file hiding among core files. Move it to quarantine.
wp-config.php is not part of the download, so open it and read it. Look for code above the opening lines or below the last line, and for any eval, base64_decode, or include pointing at a file you do not recognize.
Step 3: Reinstall plugins and themes
Reinstall each plugin and theme from its original source instead of cleaning an infected copy. It is faster, and you will not miss a subtly changed file.
wp plugin list
wp plugin install PLUGIN-SLUG --force
wp theme install THEME-SLUG --force
wp plugin verify-checksums --all
Premium plugins and themes are not on wordpress.org, so download a fresh copy from the vendor and upload it. Remove anything you do not use or recognize, and delete it instead of only deactivating it. A deactivated plugin's files can still be run directly by anyone who knows the path:
wp plugin deactivate PLUGIN-SLUG
wp plugin delete PLUGIN-SLUG
Step 4: Check the usual hiding places
Malware rarely stays in one file. Check these places even after replacing core and plugins, because they sit outside what you just reinstalled.
grep -RIl --include="*.php" -E "eval\(|base64_decode\(|gzinflate\(|str_rot13\(" wp-content
find wp-content/uploads -name "*.php"
ls -la wp-content/mu-plugins
cat .htaccess
wp cron event list
- The grep results. These functions also appear in legitimate code, so a match is a lead, not proof. Open each file and read it. Do not delete a plugin file because it matched.
wp-content/uploads. It should hold media only. Any PHP file here goes to quarantine.wp-content/mu-plugins. Must-use plugins load automatically and do not appear in the normal plugin list. A file here that you did not add is a strong warning sign..htaccess. Compare it with the default WordPress rules. Injected redirect rules, especially ones that depend on the visitor's referrer or device, are a classic hack..user.iniandphp.ini. Look for anauto_prepend_fileline. It runs a file before every page, and is a favorite place to keep a backdoor.- Theme files. Open
functions.php,header.phpandfooter.phpin your active theme and look for code you did not write. - Scheduled tasks. A cron event that calls an unfamiliar function can recreate a backdoor after you delete it. Remove it with
wp cron event delete HOOK-NAME.
For more on hidden login access, see How to Find a WordPress Backdoor.
Step 5: Clean the database
Malware often lives in the database, where a file scan never looks. Back it up first with wp db export. Your table prefix may not be wp_, so check it:
wp db prefix
wp db query "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
wp db query "SELECT ID, user_login, user_email, user_registered FROM wp_users ORDER BY user_registered DESC LIMIT 20;"
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%' OR post_content LIKE '%display:none%';"
wp db query "SELECT option_name FROM wp_options WHERE option_value LIKE '%<script%' LIMIT 50;"
siteurlandhome. Both must be your own domain. A different value redirects the whole site without changing a file.- Recent users. Any administrator you did not create can be removed with
wp user delete ID --reassign=ADMIN-ID. - Posts and options with script tags. Some contain legitimate analytics or embed code. Review each result, then clean only the injected part.
Step 6: Close the access the attacker used
If you have not changed every credential yet, do it now. The full order, from hosting access down to WordPress logins, is in Step 5 of the fix guide. Regenerate the security keys with wp config shuffle-salts so existing sessions end.
Step 7: Verify, then keep watching
wp core verify-checksums
wp plugin verify-checksums --all
find wp-content/uploads -name "*.php"
Clear any cache. Then visit the site from your phone on mobile data, arriving from a Google result, because redirect hacks often hide from direct visits. If Google flagged the site, request a review under Security issues in Search Console. Run the checks again after 24 hours and after a week. A file or admin account that returns means a backdoor or the entry point was missed.
If the malware comes back
A repeat infection almost always has one of two causes: a backdoor that was never found, or an entry point that was never closed. Re-check mu-plugins, .htaccess, .user.ini and scheduled tasks. Then work through Step 7 of the fix guide to find how the attacker got in: outdated or pirated plugins, stolen hosting credentials and server logs.
SiftWP runs file-integrity, database and malware checks like these every week over SSH, with no plugin on your site. Flagged files are quarantined, never deleted outright. Low-risk fixes are applied automatically, and anything ambiguous stops and asks you first.