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

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

Last verified: June 29, 2026
10 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 our professional WordPress security services to secure your site against vulnerabilities.

Executing this security hardening workflow requires a systematic approach that balances technical optimization with content quality. Here is how to execute each strategy effectively.


#1. Common Entry Points and Explanations of Exploitation

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. 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. XML-RPC Exploitation

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. Shared Hosting Cross-Contamination

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. Auditing Webserver Access Logs via 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.

#Identifying Attack Signatures 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.


#3. Step-by-Step Triage and Forensic Diagnostics

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: System File Inspection

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/

#4. Cleaning the Filesystem and Re-installing WordPress 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/   |
            +---------------------------------------------+

#Replacing Core System Files

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

#Re-installing 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.


#5. Identifying and Removing Database Backdoors

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

#Cleaning Injected Post Content

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%';

#Deleting Unauthorized Administrators

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;

#6. Removing Injected Cron and Configuration 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

#Programmatically Sanitizing Cron Events

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' );

#Analyzing and Disabling Persistent PHP User.ini 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.


#7. Hardening Your WordPress Architecture

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

#1. Configure File Permissions

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

#Changing Filesystem Ownership

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. Disallow File Edits 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. 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. Implementing an Immutable Container Read-Only Filesystem

For advanced setups, deploy WordPress using containerized environments (like Docker). Configure your container execution environment to mount all core directories (/wp-admin/, /wp-includes/, and root PHP files) as read-only volumes:

# docker-compose.yml example snippet
services:
  wordpress:
    image: wordpress:latest
    read_only: true # Hardens the filesystem container against write attempts
    volumes:
      - ./wp-content/uploads:/var/www/html/wp-content/uploads:rw # Only uploads folder is writable
      - ./wp-config.php:/var/www/html/wp-config.php:ro

By sealing the execution layer, even if an attacker discovers an LFI vulnerability, they cannot write script payloads to system directories, shielding the codebase from persistent infection.


#8. Action Plan: A 90-Day Security Hardening Roadmap

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? Our 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.

Article FAQ

Frequently Asked Questions

Practical answers to apply the topic in real execution.

SEO-readyGEO-readyAEO-ready4 Q&A
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, submit a review request in Google Search Console under Security & Manual Actions. For Bing, use Webmaster Tools, and submit categorizations requests to McAfee TrustedSource to clear domain warning blocks.

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

Let’s discuss

Related Articles

WordPress security audit

A real audit of an SME WordPress site: Elementor pinned at 3.11.1 with four critical CVEs, and Contact Form 7 at 5.8 exposed to CVE-2023-6449 arbitrary file upload. The outdated-plugin pattern that fast and AI-assisted builds leave behind, and how an audit catches it.