How to clean a hacked WordPress site (and why most cleanups fail within a week)
TL;DR: The instinct when you find a hacked site is to delete the malicious file and restore from backup. That sequence causes most reinfections, because it destroys the evidence you need, leaves the entry point open, and frequently restores a backup that already contains the backdoor.
The order that works: preserve, contain, find root cause, clean, harden. Root cause before cleanup is the step people skip and the one that decides whether you are done in a day or fighting the same compromise for a month. And if the site holds customer data, a compromise is a reportable personal data breach with a 72-hour clock attached, which changes what you should be doing in the first hour.
Confirming you are actually compromised
Symptoms fall into three groups, and the third is the dangerous one.
Obvious. Defacement, redirects to unrelated sites, popups you did not add, browser warnings, or Google flagging the site as deceptive.
Reported. Your host suspends the account for sending spam. A customer reports card fraud after buying from you. Search Console sends a security issue notice. Someone tells you your site is serving something odd on mobile only.
Quiet. New admin users you did not create. Unexpected scheduled tasks. Files with recent modification dates you cannot account for. Outbound connections to unfamiliar addresses. A sudden appearance of pages about pharmaceuticals or gambling in your search results while the site looks normal to you.
That last pattern is worth understanding. A large share of WordPress compromises serve malicious content conditionally: only to visitors arriving from search engines, only to mobile user agents, only to crawlers, and never to a logged-in administrator. The site looks perfect when you check it, which is exactly the intent.
Two checks that cut through this. Search site:yourdomain.com in Google and look for pages you never created. And fetch the site as a search engine crawler using Search Console's URL inspection, comparing what it sees against what your browser shows.
If those disagree, you are compromised regardless of how the site looks to you.
Before you touch anything
Three actions, in this order, before any cleanup.
Take a full snapshot. Server image, VPS snapshot, or a complete copy of files and database downloaded intact. Do not clean anything first. This is your evidence and your fallback if the cleanup goes wrong, and it costs minutes.
Store it somewhere separate and label it clearly as compromised so nobody restores it later by mistake.
Preserve logs immediately. Copy access logs, error logs and any authentication logs to a location outside the affected server. Logs rotate on a schedule that does not care about your incident, and if your host retains seven days you may be days from losing the only record of how this started.
Assess whether this is a reportable breach. If the site holds personal data, customer records, user accounts, form submissions, orders, and an attacker had access to it, this is a personal data breach under India's DPDP framework. The clock starts when you become aware, which is now, and the obligation is notification without delay plus a detailed report to the Data Protection Board within 72 hours.
You do not need proof of exfiltration. Access to a system holding personal data is the standard. The 72-hour breach response playbook covers the reporting sequence in detail, and the practical point for this hour is that your investigation now has two purposes: cleaning the site, and producing an account you can file.
Open a timestamped incident log. Plain document, every observation and action with a time against it. Reconstructing this at hour 60 is far harder than writing it as you go.
Contain without destroying evidence
Containment stops the damage continuing. It is not cleanup.
Take the site offline if it is actively harming visitors. Serving malware, redirecting to phishing, or skimming card details justifies maintenance mode or a holding page immediately. A brochure site serving spam pages to crawlers is less urgent.
Prefer a maintenance page over deleting files. The files are evidence.
Rotate credentials, in order. WordPress admin accounts. Database password, updating wp-config.php to match. Hosting control panel. FTP and SFTP. SSH keys. API keys for connected services: payment gateway, email sending, SMS, any integration. Assume everything readable from the compromised server is compromised, because it was.
Force logout of all users. Changing the security salts in wp-config.php invalidates every existing session, including the attacker's.
Check for unauthorised admin accounts. Look in the users table directly rather than only in the dashboard, since some malware hides users from the admin list. Note them before deleting, because account creation times help establish the timeline.
Tell your host. They may have infrastructure-level visibility you lack, and if the compromise originated in a shared environment or affects other sites on your account, they need to know.
Find the root cause
This is the step that decides everything. Cleanup without root cause is a delay, not a fix.
WordPress compromises almost always trace to one of five entry points.
A vulnerable plugin or theme. By a wide margin the most common. An outdated plugin with a known, published vulnerability, exploited by automated scanning that found your site indiscriminately. Check every plugin and theme against known vulnerability databases, including ones you have deactivated, because deactivated plugin files are still present and still executable if reachable directly.
Weak or reused credentials. An admin password that was guessable or that appeared in a credential dump from an unrelated breach. Check authentication logs for successful logins from unfamiliar addresses.
Outdated WordPress core. Less common than plugins but it happens.
A compromised local machine. Malware on a developer's computer stealing saved FTP credentials from an editor. Distinctive sign: file changes uploaded over FTP with no corresponding web exploitation in the access logs.
Shared hosting cross-contamination. Another site on the same account or, on poorly isolated hosting, the same server. If you host multiple sites under one account, assume all of them are affected until you check each.
How to actually find it: correlate the modification time of the earliest malicious file with your access logs at that timestamp. The request that created it is usually visible, and it names the vulnerable component. Look for POST requests to unusual paths, requests with encoded parameters, and repeated probing just before the first file change.
If your logs do not reach back far enough, you may not be able to establish this, and you should say so plainly in your breach report rather than inventing a timeline.
Where malware hides in WordPress
Knowing the common locations makes both detection and cleanup faster.
Core directories. wp-admin and wp-includes should contain only standard files. Anything unexpected there is malicious, since legitimate plugins never write into these.
The uploads folder. wp-content/uploads should contain media, not PHP. Any .php file under uploads is malicious with essentially no exceptions.
Theme files. functions.php is the most common injection target, with malicious code frequently appended at the very bottom or hidden after a long run of blank lines. Also check header.php, footer.php and index.php.
Fake plugin directories. A folder in wp-content/plugins with a plausible name that does not correspond to anything you installed, often containing a single obfuscated file.
Root files. index.php, wp-config.php, .htaccess. Malicious .htaccess rules produce the conditional redirects that only affect search traffic.
mu-plugins. Must-use plugins load automatically and do not appear in the normal plugin list, which makes this a favoured hiding place. Many site owners do not know the directory exists.
Database entries. Injected scripts in post content, malicious entries in wp_options, and scheduled tasks in the cron option.
What to look for inside files: eval(), base64_decode(), gzinflate(), str_rot13(), assert(), preg_replace with the /e modifier, long unbroken strings of encoded text, and code positioned after hundreds of blank lines so it sits off-screen in an editor.
A caution: some legitimate plugins use base64 encoding, so presence alone is not proof. Context matters, and unfamiliar encoded code in a core directory is a different finding from encoded strings inside a known commercial plugin.
| Location | What belongs there | Compromise indicator |
|---|---|---|
wp-admin/, wp-includes/ |
Core files only | Any non-core file |
wp-content/uploads/ |
Media files | Any .php file |
wp-content/plugins/ |
Plugins you installed | Unrecognised directory |
wp-content/mu-plugins/ |
Usually empty | Any file you did not add |
functions.php |
Theme code | Code after blank-line runs, encoded blocks |
.htaccess |
Rewrite rules | Conditional redirects on referrer or user agent |
wp_options |
Settings | Injected scripts, unknown cron entries |
wp_users |
Your users | Admin accounts you did not create |
wp_posts |
Content | Injected script tags, hidden links |
Cleaning: replace rather than repair
The principle throughout: replace known-good files wholesale rather than editing out malicious code. Editing leaves whatever you missed.
Replace WordPress core entirely. Download a fresh copy of the matching version and overwrite everything except wp-content and wp-config.php. This eliminates every core-directory infection in one step.
Reinstall plugins from source. Delete each plugin directory and reinstall from the repository or the vendor. Do not attempt to clean plugin files. For any plugin no longer maintained or no longer needed, delete and do not reinstall.
Reinstall the theme. If it is a commercial or repository theme, delete and reinstall from source. If it is custom, you must inspect it manually, which is why custom themes make cleanups considerably harder. Compare against version control if you have it.
Clean uploads selectively. You cannot replace this from source. Delete every .php file under uploads, and any file with an extension that does not match its content.
Rebuild .htaccess. Delete it and regenerate a clean version from WordPress settings, then re-add any custom rules you actually need from your own records.
Check wp-config.php line by line. It is short, you know what belongs in it, and it is a common injection target.
Automated scanners are useful for detection and unreliable as the sole cleanup mechanism. They find known signatures and miss novel or targeted code, and a clean scan result after a real compromise is not evidence you are finished.
The database, which people forget
A file-only cleanup that leaves the database infected produces reinfection the moment the site loads, and this is one of the most common reasons a cleanup "fails."
What to check:
wp_users. Unauthorised administrator accounts. Also check for legitimate accounts whose role was escalated to administrator.
wp_options. Injected scripts in option values, particularly anything that outputs on every page load. Check the cron option for scheduled tasks that re-download malware, which is a common persistence mechanism and the reason some sites reinfect precisely every hour.
wp_posts and wp_postmeta. Injected script tags, hidden link blocks, and spam content in posts you did not write. Search post content for <script, iframe and any domain you do not recognise.
Unfamiliar tables. Some malware creates its own storage.
Search the whole database export for suspicious patterns rather than checking tables individually. Export to SQL, open it in a proper editor, and search for eval(, base64_decode, <script, and any attacker domain you identified in the file cleanup.
Take a database backup before making changes, separate from your compromised snapshot, so a bad edit is recoverable.
Restore from backup, carefully
Restoring is often the fastest route, and it goes wrong in a specific way.
The backup must predate the compromise. If the attacker was present for six weeks, every backup from the last six weeks contains the backdoor. Restoring one reinstates the compromise, and this is precisely why sites reinfect within hours of a clean-looking recovery.
Establishing when the compromise began is therefore a prerequisite for choosing a restore point, which is another reason root cause comes before cleanup.
The vulnerability comes back with the restore. Restoring a backup from before the compromise also restores the outdated plugin that was exploited. Restore, then immediately patch, before the site is publicly reachable again.
Weigh what you lose. Restoring to a point three months back on a store means losing three months of orders. Sometimes manual cleanup, despite being slower, is the right call because the data matters more than the time.
The hybrid approach usually works best: restore core, plugins and theme from clean sources, keep your current database after cleaning it, and preserve uploads after removing anything executable.
Harden before you go back online
Cleaning without hardening returns you to the state that got you compromised.
Update everything. Core, plugins, themes. Delete anything unused, because an inactive plugin with a vulnerability is still a file on your server.
Enforce strong authentication. Unique passwords for every account, two-factor on all administrator accounts, and a review of who has admin access. Most sites have more administrators than necessary.
Limit login attempts and consider changing the login URL to reduce automated noise.
Set correct file permissions. Directories 755, files 644, wp-config.php 600 or 640. Disable file editing from the dashboard with DISALLOW_FILE_EDIT in wp-config.php, which removes a convenient path for anyone who gets admin access.
Block PHP execution in uploads at the web server level. This single rule neutralises the most common shell location.
Install file integrity monitoring so the next unexpected change alerts you on the day rather than in six weeks. This is the control that converts a long undetected compromise into a short one.
Extend log retention to ninety days minimum, shipped off the server. You now know from experience why this matters.
Fix your backups. Offsite, at a different provider, with retention long enough to predate a slow compromise. The 3-2-1 backup strategy covers the pattern.
Rotate every credential again once the site is verifiably clean, since some were rotated while the attacker may still have had access.
Recovering search visibility
If Google flagged the site, cleanup is only half the job.
Request a review in Search Console once you are confident the site is clean, describing what happened and what you fixed. Review a rejected request carefully rather than resubmitting immediately, since rejection usually means residual infection remains.
Check whether spam pages were indexed. site:yourdomain.com reveals injected pages. Return proper 404 or 410 status codes for them rather than redirecting everything to the homepage, which sends a confusing signal.
Expect a temporary ranking drop even after a clean review, and expect recovery over weeks rather than days.
If your host suspended the account, they will typically require evidence of cleanup before restoring service. Your incident log makes that conversation short.
FAQs
How do I know if my WordPress site is hacked?
Look for unauthorised admin users, unexpected file modification dates, redirects that only affect visitors from search engines or mobile devices, and pages in site:yourdomain.com results you never created. Because much WordPress malware serves malicious content conditionally and hides from logged-in administrators, compare what a search crawler sees using Search Console's URL inspection against what your browser shows.
What should I do first when my site is hacked?
Take a full snapshot before changing anything, copy logs off the server, and assess whether personal data was accessible, since that makes it a reportable breach with a 72-hour clock. Then contain: rotate credentials, invalidate sessions by changing the security salts, and take the site offline if it is actively harming visitors. Cleanup comes after preservation, not before.
Why does my site keep getting reinfected after cleanup?
Almost always because the root cause was never found, so the entry point remains open, or because the database was left infected while only files were cleaned, or because the restore came from a backup that already contained the backdoor. A scheduled task in the cron option that re-downloads malware is a common cause of reinfection on a precise interval.
Can I just restore from a backup?
Only if the backup predates the compromise, which requires knowing when the compromise began. Restoring a backup from during the attacker's presence reinstates the backdoor. Restoring one from before it also restores the vulnerable plugin that was exploited, so patch immediately after restoring and before the site becomes publicly reachable.
Where does WordPress malware usually hide?
PHP files in wp-content/uploads, which should never contain any; non-core files in wp-admin or wp-includes; appended code in functions.php, often after long runs of blank lines; fake plugin directories; the mu-plugins folder, which loads automatically and does not appear in the plugin list; malicious .htaccess rules; and injected content in wp_options, wp_posts and the cron option.
Do malware scanner plugins clean a hacked site properly?
They are useful for detection and unreliable as the only cleanup mechanism, because they match known signatures and miss novel or targeted code. A clean scan after a real compromise is not proof you are finished. Replace core, plugins and themes from known-good sources rather than editing malicious code out of existing files.
Is a hacked website a reportable data breach in India?
If the site holds personal data and an attacker had access to it, yes. Under India's DPDP framework the standard is compromise rather than proven exfiltration, so an inability to demonstrate data was taken does not make it non-reportable. Notification to affected individuals and intimation to the Data Protection Board are required without delay, with a detailed report within 72 hours of awareness.
How did my WordPress site get hacked?
Most commonly an outdated plugin or theme with a published vulnerability, found by automated scanning rather than targeted attack. Other frequent causes are weak or reused admin credentials, outdated core, malware on a developer's machine stealing saved FTP credentials, and cross-contamination from another site on the same hosting account.
Should I take my site offline while cleaning it?
Take it offline if it is actively harming visitors through malware, phishing redirects or card skimming. For a site quietly serving spam pages to crawlers it is less urgent. Use a maintenance page rather than deleting files, since the files are evidence you need for both root-cause analysis and any breach report.
How long does it take to clean a hacked WordPress site?
A straightforward compromise on a site with clean backups and good logs can be resolved in a few hours. Without logs, with a custom theme requiring manual inspection, or where root cause is unclear, it can take days. Search visibility recovery after a Google flag takes additional weeks regardless of how quickly the technical cleanup finishes.
What file permissions should WordPress use?
Directories 755, files 644, and wp-config.php at 600 or 640. Also add DISALLOW_FILE_EDIT to wp-config.php to disable dashboard-based file editing, and block PHP execution in wp-content/uploads at the web server level, which neutralises the most common location for an uploaded shell.
How do I stop this happening again?
Update core, plugins and themes promptly and delete anything unused. Enforce unique passwords with two-factor on all admin accounts and reduce the number of administrators. Set correct file permissions, block PHP in uploads, install file integrity monitoring so changes alert you the same day, extend log retention to ninety days off-server, and keep offsite backups with retention long enough to predate a slow compromise.
Conclusion
The sequence matters more than the tools. Preserve, contain, find root cause, clean, harden.
Almost every failed cleanup is a root-cause failure. The malicious file gets deleted, the site looks fine, and a week later it is back, because the vulnerable plugin that let them in was never identified and the scheduled task quietly re-downloading the payload was never found.
The database is the second most common miss. A file-only cleanup on an infected database reinfects on the next page load.
If your site holds customer data, treat this as an incident with a reporting obligation from the first hour, not a technical chore. The evidence you preserve in the first ten minutes is what makes both the cleanup and the report possible, and it is unrecoverable once you start deleting things.
Then harden properly before going back online, because a cleaned site with the same configuration is a site waiting for the same automated scanner to find it again.
HostCloud includes server-level malware scanning, extended log retention and automated daily backups restorable from the panel, on Indian infrastructure. Plans start at ₹99 a month at https://hostcloud.in. If you are mid-incident right now, contact support and preserve everything before you change a single file.
