SecurityUpdated 12 min read
How to clean a hacked WordPress site and stop it happening again
Learn how to clean hacked WordPress files, database injections, accounts, credentials and search warnings, then reduce the chance of reinfection.
By Hamza Ahmad AslamAI & WordPress Developer | Speed Optimization & Site Security

On this page
- Step 1: Contain the site and keep a copy
- Step 2: Clean the site or restore a backup?
- Step 3: Find what changed
- Verify WordPress core files
- Verify plugins that have WordPress.org checksums
- Review recently changed PHP files
- Check administrators, scheduled events and must-use plugins
- Compare wp-config.php and .htaccess with known-good copies
- Step 4: Search the database for injected scripts
- Step 5: Remove the malware from files and the database
- Step 6: Close the entry point and rotate every credential
- Step 7: Clear Google and browser security warnings
- Step 8: Watch the site for two weeks
- Step 9: When to get help
- Frequently asked questions
Short answer: To clean a hacked WordPress site, contain it and keep a copy of the compromised files and database, then find what changed and replace compromised code from trusted sources. Close the entry point, rotate credentials and salts, clear search warnings and watch for reinfection. Most steps work from a hosting control panel; the WP-CLI commands below make them faster if you have SSH.
- Contain the site and keep a copy of its files and database.
- Decide whether to restore a backup or clean the existing installation.
- Check core, plugins, themes, accounts, scheduled tasks and configuration for changes.
- Search the database for injected scripts and redirects.
- Replace compromised files and clean confirmed database injections.
- Close the entry point and rotate credentials and salts.
- Clear Google and browser security warnings.
- Watch the site for two weeks.
- Get help if you cannot regain control or stop the infection.
I have recovered about 17 hacked WordPress sites. On some of them I found malware that Sucuri and Wordfence scans had not flagged, which is why this process treats a scanner as one source of evidence and checks every layer itself. I keep client names and incidents private, so the examples below are illustrative.
Step 1: Contain the site and keep a copy
Do not start by deleting suspicious files. Keep the compromised state first, because it can show how the attacker got in, what changed and how the infection persists.
If the site is serving malware, phishing pages, harmful redirects or other dangerous content, take it offline or place it behind a maintenance response that attackers cannot bypass. A WordPress maintenance plugin may not help if compromised PHP is still running.
Without SSH, use your hosting control panel to create a snapshot or download a copy of the site's files, and export the database through phpMyAdmin. Ask your host to isolate the account if you cannot safely take the site offline yourself. Tell the host what you saw and ask whether it detected malicious processes, altered files, suspicious logins, outbound spam or account-level compromise.
With SSH and WP-CLI, export the database before changing anything:
wp db export compromised-site.sql
Take a separate copy of the files as well. A database export alone is not a complete copy of the installation.
Store the evidence copy somewhere the compromised account cannot modify it. Do not use it as a production backup later.
Investigate from a known-clean computer. If possible, work on a quarantined copy rather than the live document root. Commands that load WordPress can also load compromised code, so think before running them against an infected installation.
If the hosting account holds several sites, ask the host to check whether the compromise reaches beyond this WordPress installation.
Step 2: Clean the site or restore a backup?
The choice depends on your backups, how far back the compromise goes and whether you can identify the affected files and database records.
Restore from a backup only if it was taken before the first malicious change. A backup made after the attacker got in may already contain a backdoor, injected database content or a rogue administrator account.
A good backup does not finish the recovery. You still have to close the entry point, rotate credentials and salts, and check whether the attacker reached other parts of the hosting account.
Without SSH, check your host's backup dates and ask whether it can identify a restore point from before the first known suspicious change. Keep the current compromised state before you restore. Confirm what the restore includes, because some hosting backups handle files and databases separately.
With SSH, export the current database and copy the files before restoring a known-good copy through your normal backup or deployment process. Then run the checks in step 3 against the restored site rather than assuming the restore removed every trace.
Clean the existing installation when you can identify and replace the compromised parts, check the database and account for persistence. If you cannot establish how far the compromise goes, get help rather than deleting suspicious files one at a time.
Step 3: Find what changed
Build an inventory before removing anything. A checksum mismatch, a recently changed file, an unfamiliar administrator or an unexpected scheduled event is a lead to investigate, not proof of malware. Each check below has its own chapter, with more detail and examples, in the 32-chapter guide to fixing a hacked WordPress site.
Verify WordPress core files
Run the official WP-CLI core checksum command (opens in a new tab):
wp core verify-checksums
It compares core files with WordPress.org checksums for the installed version. Example output for a clean installation:
Success: WordPress installation verifies against checksums.
Example output for an illustrative modified file:
Warning: File doesn't verify against checksum: wp-includes/version.php
Error: WordPress installation doesn't verify against checksums.
Investigate a mismatch, then replace core from a trusted WordPress package rather than editing the suspicious file.
Without SSH, use your host's file integrity or malware scanner if it has one, or ask the host or a developer to compare the installation with a clean WordPress package. A scanner report is evidence, not proof that every file is clean.
Verify plugins that have WordPress.org checksums
Run the documented plugin checksum verification command (opens in a new tab):
wp plugin verify-checksums --all
Example output for an illustrative clean set:
Success: Verified 8 of 8 plugins.
This check works for plugins whose packages have checksums published through WordPress.org. Custom, premium or otherwise unavailable packages need comparing against a trusted vendor download or a known-good deployment artifact.
Without SSH, run your host's scanner or a security plugin such as Wordfence or Jetpack Scan, review what it finds and compare the affected components with trusted copies. A scan with no findings does not prove the site is clean.
Review recently changed PHP files
On GNU/Linux, this lists PHP files with their modification times, newest first:
find . -type f -name '*.php' -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort -r
Look closely at files changed around the suspected incident. Timestamps can be altered, so they are not a complete audit trail.
Ordinary media uploads do not need PHP files. List any PHP under the uploads directory and review each file before deleting it:
find wp-content/uploads -type f -name '*.php' -print
If the site uses a custom upload path, adjust the directory. A PHP file in uploads is suspicious, but its name and location alone do not prove it is malicious.
Without SSH, browse the files in your hosting file manager. Sort by modification date where the file manager allows it, and look at unfamiliar or recently changed PHP files in the web root, plugins, themes and uploads. Ask your host whether it can provide file change records or server logs.
Check administrators, scheduled events and must-use plugins
List administrator accounts:
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered,roles
Confirm each administrator with the site owner. An unfamiliar account may belong to a developer or agency, so identify its owner before removing it.
Inspect scheduled events:
wp cron event list --fields=hook,next_run_gmt,recurrence
Unexpected hooks can reveal persistence or spam jobs. Compare unfamiliar hooks with active plugins and custom code before removing them.
Must-use plugins load automatically and are easy to miss. List them:
wp plugin list --status=must-use --fields=name,status,version
Review every file under wp-content/mu-plugins/, including loader files that include code from another directory.
Without SSH, review the administrators under Users in wp-admin, and open wp-content/mu-plugins/ in the file manager. Ask your host or a developer to list scheduled events if your tools do not show them.
Compare wp-config.php and .htaccess with known-good copies
If you have clean copies from before the incident, compare them directly:
diff -u /secure-backup/wp-config.php wp-config.php
diff -u /secure-backup/.htaccess .htaccess
Look for new include or require statements, unexpected rewrite rules, unfamiliar bootstrap code, changed database settings and code placed before or after the normal configuration. A legitimate deployment can also change these files, so compare against release history where you have it.
Without SSH, download both files through the file manager and compare them with a trusted copy on your own computer. Never share wp-config.php publicly: it holds the database password and the site's secret keys.
Step 4: Search the database for injected scripts
Malicious content can stay in the database after you replace every compromised file. Redirects, spam pages, injected scripts and altered settings can live in posts, options or other tables.
First confirm the site's own URLs:
wp option get siteurl
wp option get home
An unexpected domain or path can cause redirects even when the files look clean.
Take a fresh database export before changing any rows. Then use read-only queries to find literal script tags. Replace wp_ if the installation uses another table prefix:
SELECT ID, post_type, post_status, post_title
FROM wp_posts
WHERE post_content LIKE '%<script%';
SELECT option_id, option_name
FROM wp_options
WHERE option_value LIKE '%<script%';
These queries produce candidates for review. Legitimate content and plugin settings can contain scripts, and an empty result does not prove the database is clean. Also look for suspicious iframes, encoded payloads and unfamiliar external domains you found earlier.
Without SSH, open phpMyAdmin from the hosting control panel, select the site's database, check the table prefix and run the same queries from the SQL tab. If SQL or serialized WordPress data is new to you, ask a developer to review the results before you edit anything.
Step 5: Remove the malware from files and the database
Replace compromised application files instead of editing them by hand. A hand edit often removes the visible payload and leaves another backdoor.
Rebuild WordPress core from an official package that matches the intended release, then update through your normal tested process. Do not carry unknown core files into the rebuilt tree.
Reinstall plugins and themes from trusted sources: a fresh package for WordPress.org plugins, the vendor download or your source-control artifact for commercial and custom code. Do not copy a plugin directory from the compromised site into the clean build because its main file looks normal.
Remove files that do not belong only after you have identified them and kept the evidence copy. Check the web root, wp-admin, wp-includes, theme and plugin directories, mu-plugins, uploads and any writable cache or temporary directories.
Remove unknown administrator accounts once you have confirmed they are not legitimate. Reassign their content where needed so you do not delete posts by accident.
Clean database injections row by row and restore known-clean content where you can. Avoid broad SQL replacements across wp_options, because option values can hold serialized data. Use WordPress itself or a serialization-aware tool to edit serialized values.
Without SSH, your host's malware cleanup service or a security plugin's repair feature can replace known files, and you can reinstall plugins and themes from wp-admin or upload fresh copies through the file manager. Neither proves that every backdoor or database injection is gone.
When you finish, run the core and plugin checksum checks again. A clean result does not prove the whole site is clean, but a new mismatch after cleanup is a strong reason to keep looking.
Step 6: Close the entry point and rotate every credential
Removing the visible malware does not stop an attacker who still has a way in. If the vulnerable plugin, stolen password, exposed hosting account or insecure deployment path remains, the site can be compromised again.
Work out the likely entry point from access logs, host alerts, the history of vulnerable components, file timestamps and the first malicious change you can date. Update the vulnerable component to a fixed release, replace it or remove it. Do not keep an abandoned plugin because the visible malware is gone.
Revoke clearly compromised access during containment. Once the code and database are clean, rotate everything from a known-clean device:
- Force password resets for WordPress users and change every privileged account's password directly.
- Rotate the database password and update
wp-config.phpin the same maintenance window. - Change hosting control panel, SSH and SFTP passwords.
- Revoke and replace API keys, application passwords, deployment tokens and webhook secrets the attacker may have read.
- Review recovery email addresses, two-factor settings, SSH authorized keys and hosting account users.
Refresh the WordPress salts with the documented WP-CLI salt command (opens in a new tab):
wp config shuffle-salts
Without SSH, generate new values at the WordPress.org secret-key service (https://api.wordpress.org/secret-key/1.1/salt/) and replace the eight keys and salts in wp-config.php through the file manager. Either way, every logged-in session ends, including the attacker's.
Do this after keeping the evidence copy, and never copy secrets from the compromised snapshot back into production. Check that the site still connects to its database after you change the database password.
For prevention, pair patching with monitoring for plugin vulnerabilities. Monitoring helps only when someone owns the alerts and removes software that is no longer maintained.
Step 7: Clear Google and browser security warnings
Once the cleaned site is live, open the Security issues report in Google Search Console. Google explains in its guidance on malware and unwanted software (opens in a new tab) that you can request a review from that report after the problem is fixed. Do not request a review while known malicious content remains.
Check browser warnings, hosting alerts, CDN notices and any blocklist named by a security product. Use each provider's own review process. One cleared warning does not mean every provider has updated.
Review indexed URLs for spam pages and unexpected titles. Remove malicious pages from the site itself first: search cleanup cannot fix a compromised origin. Keep a record of what you fixed and when, in case a provider asks for details.
Step 8: Watch the site for two weeks
Watch the recovered site closely for two weeks. A returning file, administrator account, cron hook, redirect or injected script usually means the cleanup missed persistence or the entry point is still open.
Repeat the integrity checks and compare changes with expected deployments. Review the administrator list for new accounts. Monitor uptime, page content, redirects and key templates from outside the hosting account so you see visible changes yourself.
Without SSH, review your host's alerts, run follow-up scans, check the administrator list and load important pages from a separate device or network. Note the date and result of each scan.
Keep server, authentication and application logs long enough to compare any recurrence with the original incident. If suspicious changes come back, isolate the site again and investigate from the evidence rather than deleting the symptom again.
When the emergency is over, work through the WordPress security hardening checklist on the recovered site.
Step 9: When to get help
Get help if any of these apply:
- Payment or customer data may be affected. Contact your host and your payment provider, keep the evidence and follow the incident rules that apply to you.
- The site keeps getting reinfected. Cleaning again without finding what restores the malware leaves the attacker in control.
- You cannot get into wp-admin or the hosting account. Recovery may need the host or a developer with legitimate server access.
- Your host suspended the account. Ask what triggered the suspension and what the host needs to see before restoring service.
I have restored locked-out WordPress admin access and regained access through cPanel when normal login routes failed. Some infections I cleaned reinstalled themselves after a normal cleanup, and the fix was finding and removing the mechanism that put them back.
If you want a hand, my WordPress malware removal service starts with a free assessment. A standard single-site cleanup is a fixed $199, and anything bigger is quoted before paid work starts.
Keep the evidence copy separate until the investigation is finished and the two-week watch has passed.
Frequently asked questions
Can I clean a hacked WordPress site without a backup?
Yes. Keep a copy of the current files and database before you change anything, then rebuild from trusted copies of WordPress core, plugins and themes. Without a known-good copy to compare against, the investigation takes longer, so get help if you cannot tell what was modified.
Does reinstalling WordPress core remove all malware?
No. Replacing core fixes only the core files. Malicious code can remain in plugins, themes, uploads, must-use plugins, database records, scheduled tasks or files elsewhere in the hosting account.
Is every PHP file inside wp-content/uploads malware?
No. Ordinary media uploads do not need PHP, so a PHP file there deserves a close look, but its location alone does not prove it is malicious. Check what the file does, when it appeared and whether a plugin you trust put it there.
Should I rotate passwords before or after WordPress malware removal?
Both. Revoke access you know is compromised straight away, then rotate every credential and the salts once the code and database are clean. If you rotate everything while a backdoor is still in place, the attacker can capture the new credentials.
How do I know my WordPress site is clean after malware removal?
No single scan or checksum proves it. Check the files, database, administrators, scheduled tasks, configuration and likely entry point, then repeat the checks over two weeks. If the same infection returns, look for what is restoring it instead of repeating the cleanup.
How this article was made: drafted with AI assistance from a researched brief, then checked against primary documentation before publishing.
Message
Ask me about your site
Tell me what is wrong or what you need built. I reply by email.
Share this article
Enjoyed this? Get the next article by email.
Keep reading
Security10 min read
cPGuard supply chain attack: check your server and WordPress sites
The cPGuard supply chain attack put root-level server credentials at risk through a malicious automatic update on November 2, 2026. Check your cPGuard update history first, even if you have already patched the server.
- Security
- WordPress
- Incident Response
Security11 min read
DreamHost data breach: what to check on your WordPress site now
The DreamHost data breach exposed FTP/SFTP credentials and some webmail accounts, putting affected WordPress sites at risk of unauthorized changes. Start by checking DreamHost's reset notice and securing your file-access and email accounts.
- Security
- WordPress
- Incident Response
Security12 min read
Ultra Addons for Contact Form 7 vulnerability: check, update and recover
The Ultra Addons for Contact Form 7 vulnerability (CVE-2026-102435) affects plugin versions 3.5.54 and earlier when PDF Generator is enabled and a form uses it. Version 3.5.55 fixes the flaw. Check your installed version and PDF Generator settings first, then update and look for signs of deleted files.
- WordPress
- Security
- Plugins