How to clean a hacked WordPress site

How to clean a hacked WordPress site

Last verified: October 13, 2026
14 min read
Guide
Security auditor

Expanded and up-to-date version of this topic: Clean a hacked WordPress site: malware removal, backups and hardening.

“This site may be hacked” or “Deceptive site ahead.”

Encountering a bright red Google Safe Browsing interstitial or receiving a suspension notice from your web hosting provider is every website owner’s worst nightmare. A security compromise in WordPress is not merely a cosmetic embarrassment. It threatens customer confidential data, destroys years of accumulated search engine equity through cloaked SEO spam, and risks permanent infrastructure blacklisting across global security registries.

The vast majority of attacks targeting WordPress are entirely automated. As noted in the official WordPress Hardening Documentation, opportunistic botnets continuously scan the public internet for outdated extensions, weak credentials, and unpatched theme vulnerabilities. These automated systems do not target your brand personally; they seek unhardened servers to enlist into distributed proxy networks, spam delivery rings, and credential harvesting schemes.

This operational guide details the professional, server-level malware removal methodology required to fully disinfect a compromised WordPress installation. Automated security plugins frequently mask symptoms without eliminating underlying backdoors. True remediation requires command-line precision using SSH, WP-CLI, and direct database sanitisation.

If your primary symptom consists of foreign language product pages appearing in Google Search Console, review our detailed guide on the Japanese keyword hack before executing deletions.

#Symptoms of a compromised WordPress site and initial incident triage

A security breach manifests across multiple vectors. The official documentation in FAQ: My site was hacked outlines concrete Indicators of Compromise (IoC) that confirm an active intrusion:

  • Search engines like Google and Bing flag the domain on their public malware registries.
  • Your web hosting provider suspends the account due to malicious resource utilization or outbound attack attempts.
  • Desktop antiviruses or browser security protections block customer visits.
  • Unfamiliar administrative user accounts appear in the dashboard.
  • Unexplained redirects route incoming mobile traffic or search visitors to fraudulent portals.
  • The site exhibits visible visual alterations, broken layouts, or unauthorized pop-up elements.

The mandatory first step is formal documentation. Prior to deleting or altering a single file, document everything observed: the exact timestamp of detection (including time zone), recent configuration changes (new plugins, theme adjustments, widget modifications), and environment specifications (PHP version, web server architecture, active SSH credentials). This forensic log serves as the foundation for incident remediation and saves critical hours whether you resolve the incident internally or engage external technical specialists.

Two preliminary checks must never be skipped. First, perform a full anti-malware scan on your local administrative workstation. A local keylogger or credential-stealing Trojan targeting your FTP or /wp-admin credentials renders server-side cleaning useless. Second, inform your hosting provider. On shared or multi-tenant servers, an infection can cross local account boundaries, and a site dispatching bulk spam can result in the entire server IP being blacklisted on global mail registries.

#Taking a forensic backup before modifying files

Before touching a single script, generate a comprehensive backup of the current infected state. If an overzealous script deletion accidentally purges a modified functions.php file containing critical business logic alongside the malware snippet, you will need a fallback archive for surgical extraction.

From an SSH terminal session inside the WordPress root directory, execute:

# Archive all filesystem assets into a compressed tarball
tar -czf backup-infected-files_$(date +%F).tar.gz .

# Export the raw database dump
wp db export backup-infected-db_$(date +%F).sql --allow-root

Store these forensic archives locally or within an isolated quarantine bucket for retrospective analysis.

#How to scan WordPress files for malware signatures using grep

Malware authors obscure their payloads using dynamic execution wrappers such as eval(), base64_decode(), and gzinflate(). Google Security documentation emphasizes searching for these exact functions alongside unescape() to uncover hidden script execution routines.

Execute the following pattern-matching routines from your web root to locate suspicious strings across all project files:

#Common malware signatures and patterns

  1. Locating calls to eval() (virtually never utilized in modern, standards-compliant application code):

    grep -rl --include=*.php 'eval(' .
  2. Locating unauthorized script injections and hidden iframe overlays:

    grep -rl --include=*.php '<script' .
    grep -rl --include=*.php '<iframe' .
  3. Locating encoded and obfuscated string transformations:

    grep -rl --include=*.php 'base64_decode' .
    grep -rl --include=*.php 'str_rot13' .
    grep -rl --include=*.php 'gzinflate' .
  4. Locating client-side unescape wrappers in JavaScript assets:

    grep -rl --include=*.js 'unescape(' .

Avoid passing the -w (word boundary) flag to grep when searching these patterns. The -w parameter demands non-alphanumeric trailing boundaries, causing searches for eval( to completely miss obfuscated chains like eval(base64_decode(.

#Distinguishing false alarms from active backdoors

Not every flagged occurrence indicates malicious intent:

  • Certain complex page builders or framework libraries may legitimate utilize base64_decode for serialized configuration transfers or SVG data URIs.
  • Pay strict attention to PHP scripts situated inside asset directories. Under standard WordPress architecture, the /wp-content/uploads/ directory must contain zero PHP files.

To expose all executable scripts hiding inside your uploads directory, run:

find ./wp-content/uploads -name "*.php"

Treat every single PHP file returned by this query as an active compromise until conclusively proven otherwise. Legitimate extensions store assets, documents, and media in this folder, never executable server-side scripts.

#Inspecting compromised sites through Googlebot cloaking simulation

Advanced compromises frequently implement user-agent or referer cloaking: they serve clean HTML to direct administrative visitors while injecting spam links and malicious redirects exclusively for Googlebot or search engine referrals. Evaluating the site via a standard browser fails to reveal the compromise.

Google Security guidelines caution against visiting infected sites in desktop browsers to avoid browser exploit chains. Instead, utilize curl from a terminal to simulate a Googlebot crawler arriving via a Google search referral:

curl -sv --referer "https://www.google.com/" \
  --user-agent "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
  https://example.com/ -o simulated-crawl.html

grep -iE 'eval|base64_decode|unescape|<iframe' simulated-crawl.html

Repeat the request without the referer header and compare the HTTP status codes. A 301 or 302 redirect appearing strictly when the Google referer is present confirms active cloaking rules embedded in .htaccess or early PHP runtime hooks.

#Replacing WordPress core files safely using WP-CLI

Attempting to surgically cleanse hundreds of core files in wp-admin and wp-includes is inefficient and prone to error. The definitive, reliable strategy is replacing all core system files with pristine originals.

WordPress documentation mandates reinstalling the identical version currently operating on the site to avoid fatal schema or hook mismatches. Using WP-CLI, download a fresh copy while preserving user content:

# Force-download pristine core files matching the active version
wp core download --version=$(wp core version --allow-root) --force --skip-content --allow-root

The --skip-content parameter is vital: it guarantees that your /wp-content/ directory, housing media, custom themes, and extensions, remains untouched.

Keep in mind an important limitation: standard installers overwrite existing matching filenames, but they do not automatically delete newly injected alien files. An unauthorized file like wp-includes/class-wp-session.php will survive a basic --force download. To expose these rogue scripts, execute the checksum verification detailed below.

If WP-CLI is unavailable in your hosting environment:

  1. Download a clean archive of the identical WordPress version from WordPress.org.
  2. Extract the package locally.
  3. Delete the /wp-content/ directory and wp-config-sample.php from the extracted archive.
  4. On your web server, delete the existing wp-admin and wp-includes directories entirely, upload the clean replacements via SFTP, and overwrite the root core files.

#Verifying core and plugin integrity with checksum validation

WP-CLI provides built-in utilities to compare local files against official MD5 checksums generated by WordPress.org. The command wp core verify-checksums executes on the before_wp_load hook, meaning it inspects file integrity without executing potentially compromised WordPress code:

wp core verify-checksums --allow-root

On a healthy installation, the command returns:

Success: WordPress installation verifies against checksums.

If modifications or alien files exist, the diagnostic output flags them explicitly:

Warning: File doesn't verify against checksum: wp-includes/version.php
Warning: File should not exist: wp-includes/extraneous-script.php
Error: WordPress installation doesn't verify against checksums.

The warning “File should not exist” is the primary indicator of an embedded backdoor. Passing the --include-root flag verifies the entire root directory (excluding expected exceptions like wp-config.php, .htaccess, and .maintenance). For non-English installations, pass the corresponding locale flag (such as --locale=pl_PL) to prevent translated resource files from triggering false warnings.

A corresponding verification exists for extensions via wp plugin verify-checksums:

wp plugin verify-checksums --all --allow-root

This scans every installed plugin against WordPress.org repository hashes, identifying modified files (“Checksum does not match”) and rogue injections (“File was added”). Commercial or bespoke private plugins lacking public repository listings are skipped with a warning; their integrity must be restored by downloading fresh archives directly from official vendors.

#Cleaning malicious files from the wp-content directory

The /wp-content/ hierarchy houses the vast majority of persistent malware footholds.

#Disinfecting themes and plugin directories

For all free plugins sourced from the WordPress.org repository, the most reliable practice is complete reinstallation:

# Force reinstall a clean package directly from the repository
wp plugin install contact-form-7 --force --allow-root

For premium plugins, obtain clean distribution archives from vendor portals and overwrite the directory assets over secure SFTP.

Examine your active theme. If you utilize a child theme, manually audit functions.php, header.php, index.php, and footer.php. Attackers frequently inject silent obfuscated code blocks at the very top or bottom of these templates to ensure execution on every page render.

#Purging rogue PHP scripts from the uploads folder

Because user upload directories should exclusively hold media files, you can purge executable PHP scripts directly:

# Review the list of rogue files first
find ./wp-content/uploads -name "*.php"

# Once reviewed, delete all executable PHP files from uploads
find ./wp-content/uploads -name "*.php" -delete

Always inspect the output list before executing -delete to confirm no custom caching or third-party extension has misplaced legitimate configuration files.

#Purging database tables of spam injections and unauthorised users

Malware persistence frequently extends beyond files into database tables:

  1. Rogue administrative users: Access the database CLI or phpMyAdmin and inspect user tables:

    SELECT * FROM wp_users;

    Identify accounts you do not recognize, particularly those registered with temporary domains or arbitrary character sequences. Remove them immediately.

  2. Spam injections in post content: Doorway page injections insert thousands of spam posts containing counterfeit advertisements or pharmaceutical spam. Query the wp_posts table for suspicious signatures:

    SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%';
    SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%eval(%';

If your installation suffered a Japanese Keyword Hack, thousands of auto-generated spam posts must be purged from the database, followed by sitemap resubmissions in Google Search Console to expedite deindexing.

#Auditing administrative accounts with wp user list

Querying wp_users displays account records but obscures capability roles stored in wp_usermeta. The command wp user list aggregates both tables, filtering directly by role:

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --allow-root

Cross-examine the returned roster against authorized personnel. The user_registered timestamp helps correlate unauthorized account creations with the incident timeline. On Multisite networks, verify network super-administrators as well:

wp super-admin list --allow-root

If an attacker compromised your primary administrator account and locked you out, access the database directly, update the user email in wp_users to an address you control, and trigger the standard password reset routine from the login interface.

#Inspecting wp-config.php and htaccess configuration files

Attackers frequently modify configuration files to establish persistent backdoor loaders:

  1. wp-config.php: Open the file and review every line. Attackers often insert hidden includes loading temporary payloads:

    include '/tmp/sess_f3a987...'; // SUSPICIOUS MALWARE LOADER

    Compare your configuration with wp-config-sample.php. It should contain only database connection parameters, table prefix definitions, authentication salts, and explicit system constants.

  2. .htaccess: Attackers append rewrite rules designed to intercept traffic from search engine referers while displaying normal content to direct visitors. If unauthorized directives exist, regenerate the standard WordPress rewrite structure:

    # BEGIN WordPress
    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    </IfModule>
    # END WordPress

Locate all auxiliary .htaccess files distributed across nested directories:

find . -name ".htaccess"

#Immediate hardening steps after malware eradication

Once file and database sanitisation is complete, enforce immediate preventive hardening:

  1. Rotate all authentication credentials:

    • Reset passwords across every administrative and user account.
    • Update database passwords and mirror the change inside wp-config.php.
    • Rotate hosting panel, SSH, and SFTP credentials.
  2. Regenerate cryptographic salt keys: Regenerating salts invalidates all active session cookies, instantly expelling any logged-in attackers:

    wp config shuffle-salts
  3. Disable dashboard file editing: Append to wp-config.php:

    define( 'DISALLOW_FILE_EDIT', true );

    This prevents any attacker who might obtain administrative credentials from modifying theme or plugin code directly from the administrative dashboard.

  4. Restrict XML-RPC access: Unless required by external publishing apps or Jetpack, disable XML-RPC to eliminate brute-force amplification vectors. On Apache 2.4, insert into .htaccess:

    <Files xmlrpc.php>
    Require all denied
    </Files>

#Restoring filesystem permissions and restricting database accounts

The official WordPress Hardening Guide specifies standard operational permissions: directories set to 755, files set to 644, owned by the dedicated web user account:

find /path/to/wordpress/ -type d -exec chmod 755 {} \;
find /path/to/wordpress/ -type f -exec chmod 644 {} \;

The configuration file wp-config.php should be restricted to 400 or 440, making it readable only by the owner and the web server daemon.

Under routine operation, the application database account only requires Data Manipulation Language privileges (SELECT, INSERT, UPDATE, DELETE). If multiple websites reside on the same server, guarantee that each site operates on an isolated database with an independent database user account, preventing lateral cross-site compromise.

Finally, analyze server access logs. Web server access logs provide forensic clarity on how the initial compromise occurred, detailing IP addresses, timestamps, and specific request URIs associated with file inclusion attempts or brute-force requests.

#Verifying total site disinfection over subsequent monitoring cycles

Remediation is only confirmed when the site consistently passes repeated integrity audits over subsequent days. Re-run your grep pattern scans, execute find ./wp-content/uploads -name "*.php", and run wp core verify-checksums periodically.

Re-audit database tables using wp user list --role=administrator to ensure no rogue administrators reappear. If an indicator fails or malware resurfaces, do not merely repeat the cleaning steps. Identify the initial entry vector, such as an unpatched extension, an insecure FTP password, or an unhardened server configuration, and close the vulnerability before repeating remediation.

#Reviewing Search Console security reports and requesting review

If search engines flagged your website, public warnings remain visible until formal verification succeeds. The Security Issues Report inside Google Search Console serves as the definitive reference.

The report categorizes security incidents into hacked content, malware, and social engineering. Sample URLs illustrate affected paths. Address every single flagged issue across the entire property; partial fixes will not restore search visibility.

Once all issues are resolved and verified, submit a review request via the Request Review button. An effective review submission accomplishes three tasks:

  1. Clearly explains the root cause of the compromise identified during analysis.
  2. Outlines the precise remediation steps executed across files, databases, and server configurations.
  3. Documents post-cleanup verification evidence proving the property is secure.

Review evaluations typically take from several days to several weeks. Avoid submitting multiple review requests while a decision is pending, as premature requests can introduce review delays.

#Strategic conclusions for sustained site integrity

Defending WordPress against malicious software is an ongoing engineering discipline rather than a one-off task. Lasting protection relies on proven digital hygiene:

  • Deploying timely updates across WordPress core, plugins, and parent themes.
  • Prohibiting pirated or nulled themes and plugins, which serve as primary malware distribution channels.
  • Storing regular, verified backups in isolated off-site cloud storage.

If technical remediation feels daunting or your site experiences recurring compromises, professional assistance is recommended. An incomplete cleanup leaves hidden backdoors that allow threat actors to regain persistence within days.

Review our comprehensive WordPress security audit services to identify structural vulnerabilities and protect your digital infrastructure.

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.

Related cluster

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 are the telltale indicators of a compromised WordPress installation?#
Common symptoms include security warnings in Google Search Console, unexpected spam search results, rogue PHP files inside uploads, unauthorized administrator accounts, and web host suspension notices.
What is the very first step in remediating an infected WordPress site?#
Immediately capture a full forensic backup of both the filesystem and the database before altering or deleting any files.
Why is regenerating salt keys necessary after cleaning a compromised site?#
Regenerating cryptographic salts invalidates all stored authentication cookies across active browser sessions, locking out unauthorized attackers immediately.

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

Let’s discuss

Related Articles