How to Clean a Hacked WordPress Website Step-by-Step (Malware Removal Guide)

Discovering that your WordPress website has been compromised is every site owner’s worst nightmare. Google displays a bright red warning label stating “This site may be hacked” or “Deceptive site ahead”, web browsers block visitor access, search rankings plummet overnight, and spam redirect scripts hijack legitimate visitors to malicious gambling or phishing domains.

When panic strikes, inexperienced webmasters often make fatal errors: reinstalling plugins blindly, rolling back to an infected backup, or purchasing overpriced automated scanning subscriptions that fail to eliminate persistent rootkit backdoors. On a production Linux VPS, cleaning a compromised WordPress installation requires methodical forensic analysis, clean file replacements, database sanitization, and root-level security hardening.

Phase 1: Emergency Triage & Quarantine

Before beginning forensic analysis, isolate the infected website to prevent malware from spreading to other virtual hosts or sending spam emails from your server:

  1. Take the Site Offline Safely: Block public HTTP traffic while allowing administrative SSH access by placing an Nginx maintenance block or temporary .htaccess IP whitelist:
    # In Nginx server block
    allow YOUR_ADMIN_IP;
    deny all;
    error_page 403 /maintenance.html;
  2. Create an Evidence Archive: Create a full snapshot of the infected files and database for forensic inspection before modifying anything:
    tar -czf /root/hacked_site_snapshot_$(date +%F).tar.gz /var/www/my-site/
    mysqldump -u root -p my_site_db | gzip > /root/hacked_db_$(date +%F).sql.gz
  3. Revoke All Administrative Sessions & Passwords: Change database passwords in MariaDB, reset SFTP/SSH keys, and invalidate all WordPress authentication auth salts in wp-config.php using the official WordPress.org salt generator.

Phase 2: Verifying and Replacing WordPress Core Files

Attackers frequently inject stealth backdoors into core files like index.php, wp-settings.php, and wp-includes/template-loader.php. Rather than manually inspecting thousands of files, use WP-CLI to audit core checksums against the official repository:

# Check for modified or unauthorized core files
wp core verify-checksums --path=/var/www/my-site --allow-root

If any file fails the checksum test, completely nuke and replace WordPress core while preserving your custom content:

cd /var/www/my-site
# Remove infected core directories
rm -rf wp-admin wp-includes
# Download fresh clean WordPress core
wp core download --skip-content --force --allow-root

Phase 3: Exterminating Hidden PHP Backdoors and Web Shells

Malicious actors hide persistent web shells (like c99, r57, or base64-eval droppers) inside legitimate-looking files or arbitrary folders in /wp-content/uploads/. Execute these targeted Linux grep scans to locate obfuscated payloads:

# 1. Search for dangerous execution functions
grep -rnE '(eval\(|base64_decode\(|gzinflate\(|assert\(|str_rot13\(|passthru\()' /var/www/my-site/wp-content/

# 2. Find PHP files hidden inside the uploads directory (uploads should NEVER contain PHP!)
find /var/www/my-site/wp-content/uploads/ -type f -name "*.php*"

# 3. Find files modified within the last 7 days (the infection window)
find /var/www/my-site/ -type f -mtime -7

Delete any PHP files discovered inside /wp-content/uploads/ immediately. To permanently prevent future PHP execution inside uploads, add this Nginx directive:

location ~* ^/wp-content/uploads/.*\.php$ {
    deny all;
    access_log off;
    log_not_found off;
    return 403;
}

Phase 4: Sanitizing the MariaDB / MySQL Database

Malware frequently injects rogue administrative users and malicious JavaScript tags (<script src="https://spam-tracker.biz/ad.js"></script>) directly into database tables:

# 1. Audit all administrative accounts
wp user list --role=administrator --allow-root

# 2. Delete unrecognized administrator accounts immediately
wp user delete rogue_admin_id --reassign=1 --allow-root

# 3. Scan wp_posts for injected script tags
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%' OR post_content LIKE '%eval(%';" --allow-root

# 4. Inspect siteurl and home options for malicious redirects
wp option get siteurl --allow-root
wp option get home --allow-root

Phase 5: Clearing Google Blacklists and Restoring Traffic

Once the filesystem checksums are verified clean, rogue users deleted, and database queries sanitized:

  1. Log in to Google Search Console.
  2. Navigate to Security & Manual Actions > Security issues.
  3. Click Request Review. Provide a detailed, transparent explanation: confirm that core files were reinstalled, malicious backdoors removed, credentials rotated, and server permissions locked down.
  4. Google typically reviews and removes blacklists within 24 to 48 hours, restoring your organic search impressions.

Post-Infection Prevention Protocol

Never use nulled or pirated themes and plugins—they account for over 90% of WordPress malware infections. Implement two-factor authentication (2FA) for all administrative users, enforce strict read-only permissions on wp-config.php, and configure automated offsite encrypted backups.

Advanced WordPress Malware Forensics: Rootkits, Cron Injections & Salt Invalidation

Modern WordPress malware does not rely solely on flat PHP files; attackers engineer persistent footholds across the database and server automation subsystems:

  • Hunting Malicious WP-Cron Injections: Attackers hook stealth re-infection scripts into scheduled cron tasks. Inspect active cron hooks via WP-CLI:
    wp cron event list --fields=hook,next_run,status --allow-root

    Look for suspicious randomized hook names like wp_check_file_sys_health_callback that re-download malicious payloads whenever deleted. Delete unauthorized hooks via wp cron event delete hook_name --allow-root.

  • Sanitizing wp_options Transients and Cron Arrays: Malware frequently serializes backdoor execution arrays into the cron option in wp_options. To reset the entire cron schedule cleanly:
    wp option update cron "a:0:{}" --allow-root
  • Invalidating All Active Authentication Cookies: If an attacker stole session tokens, changing passwords alone does not invalidate their active login cookies. Regenerate all security keys in wp-config.php using the official API:
    curl -s https://api.wordpress.org/secret-key/1.1/salt/

    Replace the 8 salt constants in wp-config.php immediately. This instantaneously terminates every logged-in administrative session worldwide.

  • Setting Up Inotify Real-Time File Integrity Monitoring: After cleaning, monitor the web root for unauthorized modifications using inotifywait:
    inotifywait -m -r -e create,modify,delete /var/www/my-site/wp-content/ --format '%T %e %w%f' --timefmt '%F %T' >> /var/log/wp_file_changes.log &

Host on Hardened, Secure CpanelFree VPS

Tired of shared hosting environments where cross-account contamination infects your websites? Upgrade to an isolated CpanelFree VPS with dedicated resources, hardware firewalls, and enterprise security.

Discover Hardened Secure VPS Plans →

Leave a Comment