Clean a hacked WordPress site: malware removal, backups and hardening

Clean a hacked WordPress site: malware removal, backups and hardening

Updated: October 22, 2026
19 min read
Guide
Security auditor

#Clean a hacked WordPress site: malware removal, backups and hardening

Discovering that your WordPress website has been hacked is one of the most stressful experiences a site owner or developer can face. However, panicking will not resolve the issue. By executing a structured, step-by-step forensic analysis and cleanup strategy, you can eliminate malicious code, identify the vulnerability entry points, restore database integrity, and harden your platform against future attacks. This guide outlines the exact processes, terminal commands, and database queries required to clean a compromised WordPress installation in 2026.

Explore my professional WordPress security services to secure your site against vulnerabilities. If you would rather hand the cleanup over, the WordPress malware removal page explains how I handle it.

Prevention is covered in the security hardening guide. This page covers the incident itself: finding the entry point, removing the infection and getting warnings lifted.


If the first sign was Japanese pages in Google rather than anything visible on the site, you are probably looking at the cloaked variant, which needs a different first step: see the Japanese keyword hack before deleting anything.

#1. How hackers usually get into a WordPress site

Malicious bots scour the web for known vulnerabilities, brute-force admin credentials, and exploit misconfigured webservers. To clean a site, you must first understand how the attacker gained access.

#1. Vulnerabilities in outdated plugins and themes

Vulnerabilities like SQL Injection (SQLi), Local File Inclusion (LFI), and Remote Code Execution (RCE) are frequently discovered in outdated plugins. Attackers use automated tools to scan sites for known vulnerable plugin versions and execute payloads. Two specific vulnerability classes are particularly dangerous:

  • PHP Object Injection (Deserialization): Occurs when user-supplied input is passed directly to PHP’s unserialize() function. If the codebase contains suitable “pop chain” helper classes, attackers can instantiate arbitrary PHP objects, execute code, and write files to the system.
  • Unrestricted File Uploads: If a plugin does not correctly validate files uploaded via custom forms (e.g., checks only file extension mime-types on the client side while ignoring backend verification), an attacker can upload a web shell script disguised as an image, saving it directly to /wp-content/uploads/ where they can execute it via direct HTTP access.

#2. Weak admin credentials and brute-force attacks

Bots continuously hit /wp-login.php and /xmlrpc.php with lists of common passwords. If your admin account uses a weak password, it will eventually be compromised.

#3. How attackers abuse xmlrpc.php

The legacy xmlrpc.php file allows developers to interact with the WordPress API via XML. Because it supports multi-call requests, an attacker can test hundreds of credential combinations in a single HTTP request, bypassing standard login limits.

#4. Infection from other sites on shared hosting

If your site is hosted on a shared server, a vulnerability on a neighbor’s site can allow an attacker to write files across the directory tree, infecting your clean files.


#2. What WordPress.org says to do first when a site is hacked

The WordPress.org support article FAQ My site was hacked treats the first hour as documentation work, not cleanup. Its first actionable step is to write down what you see, when you noticed it (with the time zone), and what changed recently: a new plugin, a theme edit, a widget change. That note becomes the baseline of the incident report, whether you clean the site yourself or hand it to someone else.

The same article lists indicators of compromise that point to a real hack rather than an outage: the site is blocklisted by Google or Bing, the host has disabled it, it is flagged for distributing malware, visitors’ antivirus software flags it, someone reports that it attacks other sites, or new users appear that nobody created.

For scanning, the article pairs application-level scanners (the plugins Wordfence, Sucuri, Quttera and GOTMLS) with remote scanners such as VirusTotal and Sucuri SiteCheck, because each type reports different things.

Four points from that article are easy to skip under pressure:

  • Scan your own computer too. The article notes that infections often start on the owner’s machine, where a trojan captures FTP and wp-admin credentials. New passwords typed on an infected laptop go straight to the attacker.
  • Talk to the host early. On shared hosting the hack may reach beyond one site, and a site abused to send spam can get the server IP onto email blocklists, which then hits mail sent from the same server.
  • Reinstall the same version, and not from wp-admin. The article advises against the reinstall options in the dashboard because those installers often only overwrite existing files, while hacks add new ones. It names /wp-admin and /wp-includes as the directories you can replace safely.
  • Change passwords twice. Once when you discover the hack, and again after the site is confirmed clean, for every access point: SFTP, wp-admin, the hosting panel and MySQL. The article also suggests changing the database user and password and updating wp-config.php to match.

It also names the files that deserve a manual look whatever the infection type, because a change there affects every request: .htaccess (in the root and in any subdirectory that has one), index.php, and the theme’s header.php, footer.php and functions.php.


#3. How to take a hacked WordPress site offline during cleanup

Google’s guide Quarantine your site recommends taking a hacked site offline so it stops serving content to users, and answering with a 503 status, which signals temporary downtime. Two details matter for WordPress:

  • Serve the 503 from outside the compromised tree, for example from the web server or proxy configuration your host controls. A maintenance plugin or a PHP file inside the infected installation runs next to the malware.
  • A 4xx or 5xx response on its own is not enough, because “harmful content can still be returned to users with these status codes”. A robots.txt disallow is also insufficient: it only blocks search engine crawlers, not visitors.

The guide states that taking a site offline temporarily during recovery is unlikely to affect future ranking. Its other two steps match the WordPress.org advice: tell the host, then review the user list, delete accounts the attacker created and change every password (FTP, database, system administrator and CMS).

Bring the site back online before you ask Google for a review, because the review needs pages Googlebot can crawl (section 11).


#4. How to audit WordPress access logs over SSH

To pinpoint the entry point and time of the hack, use SSH commands to audit your webserver access logs. This allows you to find malicious POST requests that correspond to file modification timestamps.

#How to find attack attempts in Nginx or Apache logs

Run these shell scripts in your terminal to parse your logs for common attack patterns:

# Find IP addresses making high-frequency POST requests to login or XML-RPC endpoints
grep -h "POST /wp-login.php" /var/log/nginx/access.log* | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 20
grep -h "POST /xmlrpc.php" /var/log/nginx/access.log* | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 20

# Search access logs for exploitation attempts on known attack endpoints
grep -E "wp-config\.php|\.env|eval\(|union\+select" /var/log/nginx/access.log

# Extract requests made by an IP address that modified files during the hack window
grep "192.168.1.100" /var/log/nginx/access.log | grep -E "POST|PUT"

Identifying the source IP and the exact script that processed the request reveals the exploited plugin or theme, allowing you to patch the vulnerability.


#5. How to triage a hacked WordPress site before cleanup

Before deleting files, preserve the state of the installation. This allows you to restore to the initial state if needed.

#Step 1: Export a forensic backup

Create a tar archive of your entire directory tree and export your database using WP-CLI:

# Create file system archive
tar -czf /tmp/compromised-files-$(date +%Y%m%d).tar.gz -C /var/www/html .

# Export database
wp db export /tmp/compromised-db-$(date +%Y%m%d).sql --path=/var/www/html

#Step 2: Find modified and suspicious PHP files

Use find and grep to scan for modified files, PHP code within uploads folders, and typical backdoor signatures (e.g., eval, base64_decode, or hidden event triggers):

# Locate PHP files inside the uploads folder (there should be none)
find /var/www/html/wp-content/uploads/ -type f -name "*.php"

# Find PHP files modified within the last 7 days
find /var/www/html/ -type f -name "*.php" -mtime -7

# Search for common obfuscation functions used by hackers
grep -rn "base64_decode(" /var/www/html/wp-content/
grep -rn "eval(" /var/www/html/wp-content/
grep -rn "gzinflate(" /var/www/html/wp-content/

#6. How to clean WordPress files and reinstall core

Do not rely on security scanners to clean core files. The most reliable method is to replace the files with clean copies from the official WordPress repository.

          +-------------------------------------------------+
          |    Infected WordPress Installation Directory     |
          +-------------------------------------------------+
                                   |
                     [ Run Core Replace Command ]
                                   |
         +-------------------------v-------------------------+
         | wp core download --force --skip-content --path=. |
         +---------------------------------------------------+
                                   |
            +----------------------v----------------------+
            | Clean core dirs: wp-admin/ & wp-includes/   |
            +---------------------------------------------+

#How to replace WordPress core files with WP-CLI

Use WP-CLI to replace all root system files and directories (wp-admin/ and wp-includes/) with clean originals, leaving your content folder untouched:

# Replace core system files
wp core download --force --skip-content --path=/var/www/html

# Validate core integrity using checksums
wp core verify-checksums --path=/var/www/html

#How to reinstall all plugins and themes

Delete and reinstall all plugins to ensure no backdoors remain hidden inside their directories:

# List all active plugins
wp plugin list --status=active --path=/var/www/html

# Force reinstall all active repository plugins
wp plugin list --field=name --path=/var/www/html | xargs -I {} wp plugin install {} --force --path=/var/www/html

For premium plugins or custom themes, delete the directories and upload clean zip packages directly from source repositories.


#7. How to read wp core verify-checksums and plugin checksum results

wp core verify-checksums downloads the md5 checksums for the installed version from WordPress.org and compares them with the files on disk. According to the command reference, it does this without loading WordPress, which matters on a site where loading WordPress runs the malware.

A clean core ends with Success: WordPress installation verifies against checksums. Each modified file produces a line such as Warning: File doesn't verify against checksum: wp-includes/version.php, and the run finishes with Error: WordPress installation doesn't verify against checksums.

Options that help after a hack:

  • --include-root also checks the root directory and warns if any non-WordPress items are found there, which is where dropped loaders next to wp-load.php show up.
  • --version=<version> and --locale=<locale> compare against a specific release and language build.
  • --format=json returns machine-readable output for a script or an incident ticket.
  • --insecure retries without certificate validation. The reference warns that this makes the request vulnerable to a man-in-the-middle attack, so leave it out on a compromised server.

wp core download --force only “overwrites existing files, if present”, per the download reference. Files an attacker added inside wp-admin/ or wp-includes/ can survive it. The safer order is to delete those two directories, which the WordPress.org FAQ calls safe to replace, then download and verify.

For plugins, wp plugin verify-checksums --all checks plugin files against WordPress.org’s checksums, and --strict makes even “soft changes” such as readme.txt edits fail (reference). Because the checksums come from WordPress.org, the command covers plugins from the WordPress.org directory. Premium and custom plugins need a fresh package from the vendor or from your own repository.

wp core verify-checksums --include-root --path=/var/www/html
wp plugin verify-checksums --all --strict --path=/var/www/html

#8. How to find and remove WordPress database backdoors

Hackers often inject malicious administrator accounts, script tags, and spam content directly into your database.

#How to find injected scripts in wp_posts and wp_options

The Japanese Keyword Hack and Pharma Hack inject thousands of spam pages and redirect links into post content fields.

-- Search for script tags and obfuscated code in post content
SELECT ID, post_title, post_date FROM wp_posts 
WHERE post_content LIKE '%<script%' OR post_content LIKE '%eval(%';

-- Search options table for suspicious options or auto-load scripts
SELECT option_name, option_value FROM wp_options 
WHERE option_name LIKE '%hack%' OR option_value LIKE '%base64_decode%';

#How to find and delete fake admin accounts

Identify and remove newly registered admin accounts:

-- List all administrators registered recently
SELECT ID, user_login, user_email, user_registered 
FROM wp_users 
WHERE ID IN (
    SELECT user_id FROM wp_usermeta 
    WHERE meta_key = 'wp_capabilities' AND meta_value LIKE '%administrator%'
);

-- Delete admin user with ID 99 (verify the ID first)
DELETE FROM wp_users WHERE ID = 99;
DELETE FROM wp_usermeta WHERE user_id = 99;

#9. How to remove malicious WordPress cron and config backdoors

Malware often schedules cron jobs to re-infect clean files. This means that even if you replace all system files, the site may be re-infected within hours.

#Auditing WordPress cron tasks

All scheduled tasks are stored as serialized arrays in the wp_options table under the option name cron. Use this WP-CLI command to inspect active scheduled events:

# List all scheduled cron events
wp cron event list --path=/var/www/html

#How to delete a malicious cron event with PHP

If you identify an unknown cron hook (e.g., wp_update_system_cache referencing a dynamic code block), use this PHP script to delete it:

declare(strict_types=1);

namespace WPPoland\Security\Cron;

/**
 * Removes malicious cron hooks from the schedule.
 */
function remove_malicious_cron_event(): void {
    $target_hook = 'wp_update_system_cache';
    
    // Clear the hook if it is scheduled
    $timestamp = wp_next_scheduled( $target_hook );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, $target_hook );
        error_log( sprintf( 'Security Action: Unscheduled backdoor hook "%s".', $target_hook ) );
    }
}
add_action( 'init', __NAMESPACE__ . '\\remove_malicious_cron_event' );

#How to find .user.ini and .htaccess backdoors

A common persistence mechanism is the injection of .user.ini or .htaccess configuration file overrides in the site root directory. Attackers define PHP runtime directives like auto_prepend_file to execute a malware script before any core WordPress code loads:

; Malicious .user.ini backdoor
auto_prepend_file = "/var/www/html/wp-content/uploads/2026/06/backdoor.jpg"

Because this file executes globally, even running empty WordPress plugins or requesting static login screens triggers the backdoor code. Ensure you search your site root for .user.ini and .htaccess file modifications and verify that no unauthorized scripts are prepended or appended.


#10. How to harden WordPress after a malware cleanup

Once your site is clean, apply these security configurations to prevent future attacks:

#1. Which file permissions to set for WordPress

Set restrictive file permissions across your directory structure. The webserver user (www-data) should not have write permissions to root files:

# Set directories to 755
find /var/www/html/ -type d -exec chmod 755 {} \;

# Set files to 644
find /var/www/html/ -type f -exec chmod 644 {} \;

# Set wp-config.php to read-only for owner (400)
chmod 400 /var/www/html/wp-config.php

#Who should own WordPress files

In addition to permissions, ensure correct file ownership. The webserver process (e.g., www-data or apache) should not own the WordPress PHP files. Instead, set the owner to a separate deployer user (e.g., wp-user), and configure the group to www-data. This prevents a compromised web server process from writing to or modifying any .php files inside the system structure:

# Change owner to deployer user and group to web server
chown -R wp-user:www-data /var/www/html/

# Allow web server write access only to uploads folder
chown -R www-data:www-data /var/www/html/wp-content/uploads/

#2. How to disable file editing in wp-config.php

Add these directives to your wp-config.php file to disable the built-in theme/plugin editor and block installation of new plugins:

// Disable the built-in file editor
define( 'DISALLOW_FILE_EDIT', true );

// Block plugin and theme installations or updates via admin dashboard
define( 'DISALLOW_FILE_MODS', true );

// Force secure HTTPS connections for administrative logins
define( 'FORCE_SSL_ADMIN', true );

#3. How to rotate WordPress salts

Rotate your authentication salts to invalidate all active user sessions and cookies. This automatically logs out any attackers:

# Shuffle authentication salts securely
wp config shuffle-salts --path=/var/www/html

#4. Read-only filesystem for WordPress in Docker

The official image documentation (docker-library/docs) describes a static deployment: build a custom image whose code lives in /usr/src/wordpress, then run it read-only with writable storage only for /tmp, /run and, optionally, wp-content/uploads. By default the image keeps WordPress in a writable /var/www/html volume and updates itself there, so read_only: true on the stock image does not give you this protection.

FROM wordpress:apache
WORKDIR /usr/src/wordpress
RUN set -eux; \
	find /etc/apache2 -name '*.conf' -type f -exec sed -ri -e "s!/var/www/html!$PWD!g" -e "s!Directory /var/www/!Directory $PWD!g" '{}' +; \
	cp -s wp-config-docker.php wp-config.php
COPY custom-theme/ ./wp-content/themes/custom-theme/
COPY custom-plugin/ ./wp-content/plugins/custom-plugin/
# docker-compose.yml: run the custom image read-only
services:
  wordpress:
    image: custom-wordpress:tag
    read_only: true
    tmpfs:
      - /tmp
      - /run
    volumes:
      - ./uploads:/usr/src/wordpress/wp-content/uploads

With the code layer sealed, a file-write bug can only reach the uploads mount, which is why the uploads checks earlier in this guide still apply. Updates then mean rebuilding and redeploying the image, and the image docs remind you to do that regularly to get WordPress security updates.

#5. What the WordPress hardening guide adds after a cleanup

The Hardening WordPress guide sets wp-config.php to 400 or 440, so only you and the web server can read it. It notes that DISALLOW_FILE_EDIT “will not prevent an attacker from uploading malicious files to your site, but might stop some attacks”. For the database user, read and write privileges are enough for normal operation, but the guide advises against revoking structural privileges such as ALTER, because some updates and plugins change the schema. On monitoring, it recommends running file monitoring as root so an attacker cannot easily switch it off, watching only executable file types such as .php to cut noise, and keeping regular snapshots so you hold copies from both before and after a compromise.


#11. How Google reports a hacked site and how long a review takes

Google’s findings appear in Search Console in the Security issues report (Search Console Help). Affected pages can get a warning label in search results or an interstitial warning in the browser. The report uses four hacked categories:

Issue in the reportGoogle’s descriptionWhere to look in WordPress
Hacked: MalwareThe site is infected by, or hosting, malwareuploads, plugins, .user.ini, .htaccess
Hacked: Code injectionMalicious code injected into pagestheme files, wp_options, scripts in wp_posts
Hacked: Content injectionSpammy links or text added to pageswp_posts content
Hacked: URL injectionNew pages created, often with spammy words or linksnew posts, rogue PHP files, sitemaps

The same page sets three rules. Fix the issue throughout the site, because fixing only some pages does not earn a partial return to search results. Select Request Review only when every listed issue is fixed on all pages. Do not resubmit while a request is pending. A good request explains the exact issue, describes the steps taken to fix it, and documents the outcome.

Google’s Request a review guide adds the prerequisites: verified ownership in Search Console, a cleaned site, the vulnerability fixed, and the site back online so Googlebot can crawl it. Check the homepage and a URL the attacker modified with cURL or Wget first. A request sent while the problem persists extends the time the site stays flagged.

Issue typeReview time according to Google
Hacked with spamup to several weeks
Malwarea few days
Phishingabout a day

If Google finds the site clean, browser and search warnings are removed within 72 hours. If not, the report may list more sample URLs and the warnings stay. Google is not the only list: the WordPress.org FAQ recommends registering the site with Bing Webmaster Tools, Yandex Webmaster and Norton Safe Web as well, since visitors may rely on any of them.

On 20 October 2026 Google announced two tiers of Safe Browsing site warnings in Chrome for users with Enhanced Protection mode turned on: “suspicious” sites (a lower-confidence verdict, possible risk of user harm) and “dangerous” sites (a high-confidence verdict, high risk of user harm) (Google, 20 October 2026). Dangerous sites keep the full-page red interstitial. A suspicious-site warning differs in format, icon, colour and text, and Google shows it when a page exhibits deceptive patterns commonly associated with known malicious sites.


#12. What a 90-day WordPress security hardening plan looks like

Maintain a secure platform with this 90-day checklist:

  • Days 1-30 (Immediate Triage): Execute a comprehensive file integrity audit using WP-CLI to detect modified core files. Rotate all database passwords, database root credentials, and SSH/SFTP access keys. Rotate the authentication salts in wp-config.php to terminate active sessions, and disable direct theme/plugin file editing by adding DISALLOW_FILE_EDIT to your configuration file.
  • Days 31-60 (Forensics & Firewalls): Audit active WordPress cron tasks to verify that no malicious scheduled functions persist. Analyze webserver access logs for anomalous behavior, POST spikes to XML-RPC, or LFI execution patterns. Implement a server-level Web Application Firewall (WAF) or use a reverse proxy (e.g., Cloudflare) to block SQLi and XSS payloads at the edge.
  • Days 61-90 (Hardening & Auditing): Configure automated offsite backups to a secure, write-once-read-many (WORM) storage bucket. Conduct static code analysis and penetration testing on custom themes and proprietary plugins. Schedule quarterly automated security scanning runs and establish an incident response protocol for your operations team.

Need help cleaning a compromised site or auditing your platform security? My WordPress security team can identify entry points, clean files, and secure your database. Contact us to discuss your project requirements.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

What should be checked first on a hacked WordPress site?#
Start with the filesystem, recently changed files, unknown administrator accounts, suspicious database content, and server logs. Do not reinstall plugins blindly before preserving enough evidence to understand the entry point.
Can a malware cleanup be rolled back?#
Cleanup work should start from a backup and a file snapshot. Some changes, such as deleting infected files or rotating credentials, are intentional, so rollback means restoring from a known clean point rather than undoing every step.
What usually matters after the visible malware is removed?#
Passwords, salts, plugin versions, writable directories, cron jobs, redirects, and Search Console warnings all need review. Otherwise the site may look clean while the same weakness remains open.
How do I handle blacklist removal requests with security providers?#
Once you confirm that all malware is removed and the filesystem is clean, request a review in the Security issues report in Google Search Console. Google lists review times from about a day for phishing to several weeks for spam hacks. Other lists, such as Bing Webmaster Tools, Yandex Webmaster and Norton Safe Web, run their own reviews.

Need an FAQ tailored to your industry and market? I can build one aligned with your business goals.

Let’s discuss

Related Articles