<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MD Pabel</title>
    <description>The latest articles on DEV Community by MD Pabel (@md_pabel_fe07e07449db7326).</description>
    <link>https://dev.to/md_pabel_fe07e07449db7326</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3343403%2F5343d879-bb08-485f-b057-6987204f4891.jpg</url>
      <title>DEV Community: MD Pabel</title>
      <link>https://dev.to/md_pabel_fe07e07449db7326</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/md_pabel_fe07e07449db7326"/>
    <language>en</language>
    <item>
      <title>WordPress Malware Removal Is Not Just “Delete the Bad File</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Sat, 29 Aug 2026 20:00:25 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/wordpress-malware-removal-is-not-just-delete-the-bad-file-4b06</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/wordpress-malware-removal-is-not-just-delete-the-bad-file-4b06</guid>
      <description>&lt;p&gt;When a WordPress site is hacked, the obvious approach is usually:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the malicious file&lt;/li&gt;
&lt;li&gt;Delete it&lt;/li&gt;
&lt;li&gt;Run a malware scanner&lt;/li&gt;
&lt;li&gt;Assume the site is clean&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That works for simple infections.&lt;/p&gt;

&lt;p&gt;But in more advanced cases, the malicious file you can see is only one part of the compromise.&lt;/p&gt;

&lt;p&gt;The real problem is often &lt;strong&gt;persistence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A site can look clean after remediation and then become infected again because another component is still capable of restoring the malware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Malware can exist across several WordPress layers
&lt;/h2&gt;

&lt;p&gt;In real cleanup cases, I have found malicious code or persistence mechanisms in places like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wp-content/mu-plugins/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fake WordPress plugins&lt;/li&gt;
&lt;li&gt;Theme &lt;code&gt;functions.php&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Database &lt;code&gt;options&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;Cron jobs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.user.ini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Service workers&lt;/li&gt;
&lt;li&gt;Compromised uploads&lt;/li&gt;
&lt;li&gt;Random PHP files inside &lt;code&gt;wp-content&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Modified WordPress core files&lt;/li&gt;
&lt;li&gt;Injected JavaScript&lt;/li&gt;
&lt;li&gt;Hidden administrator accounts&lt;/li&gt;
&lt;li&gt;Hosting-level configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that these components may not operate independently.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cron
  ↓
Malicious PHP file
  ↓
Database option
  ↓
Injected JavaScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting only the PHP file may solve the visible symptom temporarily while leaving the restoration mechanism untouched.&lt;/p&gt;

&lt;h2&gt;
  
  
  A clean scan is not proof that the site is clean
&lt;/h2&gt;

&lt;p&gt;Malware scanners are useful.&lt;/p&gt;

&lt;p&gt;I use them too.&lt;/p&gt;

&lt;p&gt;But a scanner result should be treated as a &lt;strong&gt;signal&lt;/strong&gt;, not absolute proof.&lt;/p&gt;

&lt;p&gt;During a proper investigation, I also want answers to questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Were any administrator accounts created?&lt;/li&gt;
&lt;li&gt;Were plugin files modified?&lt;/li&gt;
&lt;li&gt;Is there an unfamiliar fake plugin?&lt;/li&gt;
&lt;li&gt;Are there suspicious entries in the database?&lt;/li&gt;
&lt;li&gt;Are cron jobs loading unknown code?&lt;/li&gt;
&lt;li&gt;Has &lt;code&gt;.user.ini&lt;/code&gt; been modified?&lt;/li&gt;
&lt;li&gt;Is another website in the same hosting account compromised?&lt;/li&gt;
&lt;li&gt;Are credentials still exposed?&lt;/li&gt;
&lt;li&gt;Are SEO spam URLs still indexed?&lt;/li&gt;
&lt;li&gt;Is the domain blacklisted?&lt;/li&gt;
&lt;li&gt;What caused the compromise in the first place?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And most importantly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What can recreate the malware after I delete it?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question is often more valuable than simply asking where the malware is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinfection does not always mean the attacker hacked the site again
&lt;/h2&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;Suppose you remove a malicious file at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-content/uploads/example.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few hours later, it appears again.&lt;/p&gt;

&lt;p&gt;It is tempting to conclude:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The attacker got back into the site.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe.&lt;/p&gt;

&lt;p&gt;But another possibility is that something already present on the server restored it.&lt;/p&gt;

&lt;p&gt;That could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.user.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or a malicious database entry, fake plugin, &lt;code&gt;mu-plugin&lt;/code&gt;, theme injection, or another PHP loader.&lt;/p&gt;

&lt;p&gt;In that situation, the second infection is not necessarily a second intrusion.&lt;/p&gt;

&lt;p&gt;It is the original compromise continuing to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  One recent case used multiple persistence paths
&lt;/h2&gt;

&lt;p&gt;I recently investigated a WordPress infection where malicious components were distributed across several locations.&lt;/p&gt;

&lt;p&gt;The persistence involved areas such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mu-plugins
fake plugin
theme functions.php
database options table
cron
.user.ini
service workers
wp-content files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Removing one component was not enough.&lt;/p&gt;

&lt;p&gt;Several pieces could contribute to restoring or loading other malicious components.&lt;/p&gt;

&lt;p&gt;This kind of case changes the way you approach remediation.&lt;/p&gt;

&lt;p&gt;Instead of thinking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find malware → Delete malware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the workflow becomes closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Detect symptoms
      ↓
Map compromised components
      ↓
Identify relationships
      ↓
Remove persistence
      ↓
Remove payloads
      ↓
Find initial access
      ↓
Verify cleanup
      ↓
Monitor for reinfection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My general WordPress malware cleanup workflow
&lt;/h2&gt;

&lt;p&gt;I prefer to separate remediation into several stages.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Confirm the compromise
&lt;/h3&gt;

&lt;p&gt;Before deleting anything, identify the symptoms.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redirects&lt;/li&gt;
&lt;li&gt;Spam pages&lt;/li&gt;
&lt;li&gt;Unknown JavaScript&lt;/li&gt;
&lt;li&gt;New admin accounts&lt;/li&gt;
&lt;li&gt;Browser warnings&lt;/li&gt;
&lt;li&gt;Google Safe Browsing alerts&lt;/li&gt;
&lt;li&gt;Suspicious PHP files&lt;/li&gt;
&lt;li&gt;Modified plugin files&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Determine the scope
&lt;/h3&gt;

&lt;p&gt;Do not assume only one website or one directory is affected.&lt;/p&gt;

&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress files
database
hosting account
other websites
server configuration
DNS/CDN
administrator accounts
scheduled tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Investigate persistence
&lt;/h3&gt;

&lt;p&gt;This is one of the most important steps.&lt;/p&gt;

&lt;p&gt;Search for anything capable of restoring another malicious component.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Remove malicious components
&lt;/h3&gt;

&lt;p&gt;Only after understanding the relationships between them should cleanup begin.&lt;/p&gt;

&lt;p&gt;Otherwise you can end up deleting symptoms while leaving the mechanism alive.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Investigate the entry point
&lt;/h3&gt;

&lt;p&gt;Potential causes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vulnerable plugins&lt;/li&gt;
&lt;li&gt;Stolen credentials&lt;/li&gt;
&lt;li&gt;Abandoned plugins&lt;/li&gt;
&lt;li&gt;Compromised hosting accounts&lt;/li&gt;
&lt;li&gt;Exposed admin access&lt;/li&gt;
&lt;li&gt;Another infected site under the same account&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Recover reputation and search visibility
&lt;/h3&gt;

&lt;p&gt;Some infections continue causing problems even after the server is clean.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indexed Japanese SEO spam&lt;/li&gt;
&lt;li&gt;Google Safe Browsing warnings&lt;/li&gt;
&lt;li&gt;Search Console security issues&lt;/li&gt;
&lt;li&gt;Spam URLs remaining in search results&lt;/li&gt;
&lt;li&gt;Domain reputation problems&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Monitor after cleanup
&lt;/h3&gt;

&lt;p&gt;A successful cleanup is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The scanner says zero malware.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better standard is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The malicious components are gone, the persistence paths are gone, the likely entry point has been addressed, and the site remains clean afterward.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  I turned this workflow into a book
&lt;/h2&gt;

&lt;p&gt;After working on WordPress malware cleanup for years, I started documenting the process I use across real cases.&lt;/p&gt;

&lt;p&gt;That eventually became:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WordPress Malware Removal for Developers &amp;amp; Site Owners&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It covers areas including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Diagnosing compromised WordPress sites&lt;/li&gt;
&lt;li&gt;Determining infection scope&lt;/li&gt;
&lt;li&gt;File-system cleanup&lt;/li&gt;
&lt;li&gt;Database malware&lt;/li&gt;
&lt;li&gt;Redirect malware&lt;/li&gt;
&lt;li&gt;SEO spam&lt;/li&gt;
&lt;li&gt;Fake plugins and themes&lt;/li&gt;
&lt;li&gt;Hidden administrator accounts&lt;/li&gt;
&lt;li&gt;Persistence and reinfection&lt;/li&gt;
&lt;li&gt;Hosting and server issues&lt;/li&gt;
&lt;li&gt;Blacklist recovery&lt;/li&gt;
&lt;li&gt;Post-cleanup monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The focus is on practical investigation rather than simply installing another security plugin.&lt;/p&gt;

&lt;p&gt;The book is currently available using a &lt;strong&gt;Pay What You Want&lt;/strong&gt; model, starting at &lt;strong&gt;$9.99&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mdpabel.com/books/wordpress-malware-removal/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=book_promo&amp;amp;utm_content=malware_layers" rel="noopener noreferrer"&gt;https://www.mdpabel.com/books/wordpress-malware-removal/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=book_promo&amp;amp;utm_content=malware_layers&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;When cleaning a hacked WordPress site, do not stop after finding the first malicious file.&lt;/p&gt;

&lt;p&gt;The more useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What else must still exist for this infection to come back?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finding that answer is often where the real malware removal work begins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggested DEV tags:&lt;/strong&gt; &lt;code&gt;wordpress&lt;/code&gt;, &lt;code&gt;security&lt;/code&gt;, &lt;code&gt;cybersecurity&lt;/code&gt;, &lt;code&gt;webdev&lt;/code&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>wordpressmalwareremoval</category>
      <category>books</category>
      <category>security</category>
    </item>
    <item>
      <title>I’m Writing a Practical WordPress Malware Removal Book Based on Real Cleanup Cases</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Sat, 29 Aug 2026 19:58:48 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/im-writing-a-practical-wordpress-malware-removal-book-based-on-real-cleanup-cases-2j30</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/im-writing-a-practical-wordpress-malware-removal-book-based-on-real-cleanup-cases-2j30</guid>
      <description>&lt;h1&gt;
  
  
  I’m Writing a Practical WordPress Malware Removal Book Based on Real Cleanup Cases
&lt;/h1&gt;

&lt;p&gt;A WordPress site can look completely normal to the owner while visitors are being redirected somewhere else.&lt;/p&gt;

&lt;p&gt;A blank screen can be malware.&lt;/p&gt;

&lt;p&gt;It can also be an ordinary PHP error.&lt;/p&gt;

&lt;p&gt;A security scanner can find a suspicious file without telling you how it got there, what created it, or whether deleting it actually solved the compromise.&lt;/p&gt;

&lt;p&gt;These are some of the reasons I started writing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WordPress Malware Removal for Developers &amp;amp; Site Owners&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The book is now available in &lt;strong&gt;Early Access on Leanpub&lt;/strong&gt;, with Chapter 1 available as a free sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I’m Writing This Book
&lt;/h2&gt;

&lt;p&gt;I’ve been working with WordPress sites since 2018 through freelance marketplaces, agencies, direct client projects, and my own company, 3Zero Digital.&lt;/p&gt;

&lt;p&gt;During that time, I’ve investigated and cleaned more than &lt;strong&gt;4,500 hacked WordPress sites&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The incidents were rarely as simple as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run a malware scanner → delete the infected file → change the password → done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Real cases are usually messier.&lt;/p&gt;

&lt;p&gt;I’ve worked on sites where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;desktop visitors saw a normal website while mobile visitors were redirected&lt;/li&gt;
&lt;li&gt;malware was stored in the WordPress database rather than an obvious PHP file&lt;/li&gt;
&lt;li&gt;administrators existed in the database but were hidden from the WordPress Users screen&lt;/li&gt;
&lt;li&gt;fake plugins were designed to look harmless&lt;/li&gt;
&lt;li&gt;SEO spam appeared in Google without being obvious inside WordPress&lt;/li&gt;
&lt;li&gt;malicious behavior came back after the first cleanup&lt;/li&gt;
&lt;li&gt;checkout pages loaded suspicious remote code&lt;/li&gt;
&lt;li&gt;the WordPress files looked clean while the problem existed at the hosting, DNS, or CDN layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That experience shaped the main idea behind the book:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Finding malware is not the same as understanding what happened.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Start With the Symptom, Not the Word “Malware”
&lt;/h2&gt;

&lt;p&gt;One of the first lessons in the book is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Hacked” is a conclusion. The symptom is what you actually observed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the site redirects&lt;/li&gt;
&lt;li&gt;the screen is blank&lt;/li&gt;
&lt;li&gt;Google shows pages you never created&lt;/li&gt;
&lt;li&gt;an administrator is missing&lt;/li&gt;
&lt;li&gt;checkout suddenly behaves differently&lt;/li&gt;
&lt;li&gt;a visitor gets a warning&lt;/li&gt;
&lt;li&gt;a deleted infection keeps returning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each symptom may have a security explanation.&lt;/p&gt;

&lt;p&gt;But many of them can also have ordinary explanations.&lt;/p&gt;

&lt;p&gt;A blank page could be caused by malicious code replacing &lt;code&gt;index.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or it could be caused by a PHP/theme mistake.&lt;/p&gt;

&lt;p&gt;The symptom alone does not decide which one is true.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence does.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Site Looking Normal Does Not Prove It Is Clean
&lt;/h2&gt;

&lt;p&gt;One of the real cases used in Chapter 1 involved a website that appeared completely normal from the owner's desktop.&lt;/p&gt;

&lt;p&gt;But a request using a mobile browser identity received a redirect to an external domain.&lt;/p&gt;

&lt;p&gt;The malicious rule was checking the visitor before deciding what response to send.&lt;/p&gt;

&lt;p&gt;That means checking the homepage once from your normal browser is a very weak security test.&lt;/p&gt;

&lt;p&gt;A compromised site may behave differently depending on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mobile vs desktop&lt;/li&gt;
&lt;li&gt;logged-in vs logged-out users&lt;/li&gt;
&lt;li&gt;first visit vs returning visit&lt;/li&gt;
&lt;li&gt;direct traffic vs Google traffic&lt;/li&gt;
&lt;li&gt;cookies&lt;/li&gt;
&lt;li&gt;referrer&lt;/li&gt;
&lt;li&gt;IP or geographic conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why WordPress malware investigation often requires looking beyond the WordPress dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Book Covers
&lt;/h2&gt;

&lt;p&gt;The current plan includes 16 chapters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is the WordPress Site Actually Hacked?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How Does a WordPress Site Get Hacked?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What to Do Before Cleaning Malware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find What Else Is Infected&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove Malware from WordPress Files&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove Malware from the WordPress Database&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean the Hosting Account, Server, and DNS&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find and Remove WordPress Redirect Malware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove WordPress SEO Spam and Hacked URLs From Google&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find and Remove Fake WordPress Plugins and Themes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove Unwanted and Hidden WordPress Administrators&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find and Remove WooCommerce Credit-Card Skimming Malware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosting and Domain Suspension Removal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blacklist Removal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop Malware from Coming Back&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure and Monitor a Recovered Site&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is to follow an incident from the first suspicious symptom through investigation, cleanup, recovery, and verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Not Meant to Be a Theory-Only Security Book
&lt;/h2&gt;

&lt;p&gt;I’m trying to keep the book practical.&lt;/p&gt;

&lt;p&gt;The examples come from real cleanup and investigation experience.&lt;/p&gt;

&lt;p&gt;That includes looking at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress files&lt;/li&gt;
&lt;li&gt;plugins and themes&lt;/li&gt;
&lt;li&gt;database records&lt;/li&gt;
&lt;li&gt;users and capabilities&lt;/li&gt;
&lt;li&gt;browser source and Network requests&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;cron jobs&lt;/li&gt;
&lt;li&gt;hosting accounts&lt;/li&gt;
&lt;li&gt;DNS and CDN settings&lt;/li&gt;
&lt;li&gt;Google search results&lt;/li&gt;
&lt;li&gt;WooCommerce checkout behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also try to separate what the evidence proves from what it only suggests.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;A suspicious loader contacting a remote server is evidence of suspicious behavior.&lt;/p&gt;

&lt;p&gt;It is not automatically proof that payment information was stolen.&lt;/p&gt;

&lt;p&gt;A malicious file proves that malicious code exists.&lt;/p&gt;

&lt;p&gt;It does not automatically prove how the attacker originally entered the site.&lt;/p&gt;

&lt;p&gt;Those distinctions matter during real incident response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Early Access
&lt;/h2&gt;

&lt;p&gt;The book is being published progressively on Leanpub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 1 is available now as a free sample&lt;/strong&gt;, and new chapters will be added as they are completed.&lt;/p&gt;

&lt;p&gt;Early Access readers receive future chapters and updates to the same edition as they are released.&lt;/p&gt;

&lt;p&gt;You can read the free sample or follow the book here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mdpabel.com/books/wordpress-malware-removal/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=book_promo&amp;amp;utm_content=malware_layers" rel="noopener noreferrer"&gt;https://www.mdpabel.com/books/wordpress-malware-removal/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=book_promo&amp;amp;utm_content=malware_layers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m also interested in feedback from WordPress developers, hosting professionals, security researchers, and site owners who have dealt with compromised WordPress sites.&lt;/p&gt;

&lt;p&gt;What part of WordPress malware cleanup do you think causes the most confusion?&lt;/p&gt;




&lt;h1&gt;
  
  
  wordpress #security #webdev #cybersecurity
&lt;/h1&gt;

</description>
      <category>wordpress</category>
      <category>cybersecurity</category>
      <category>website</category>
    </item>
    <item>
      <title>WordPress Malware Removal for Developers &amp; Site Owners — Built from Real Cleanup Cases</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Mon, 17 Aug 2026 21:46:34 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/wordpress-malware-removal-for-developers-site-owners-built-from-real-cleanup-cases-3f2b</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/wordpress-malware-removal-for-developers-site-owners-built-from-real-cleanup-cases-3f2b</guid>
      <description>&lt;h1&gt;
  
  
  I’m Writing a Practical WordPress Malware Removal Book Based on Real Cleanup Cases
&lt;/h1&gt;

&lt;p&gt;A WordPress site can look completely normal to the owner while visitors are being redirected somewhere else.&lt;/p&gt;

&lt;p&gt;A blank screen can be malware.&lt;/p&gt;

&lt;p&gt;It can also be an ordinary PHP error.&lt;/p&gt;

&lt;p&gt;A security scanner can find a suspicious file without telling you how it got there, what created it, or whether deleting it actually solved the compromise.&lt;/p&gt;

&lt;p&gt;These are some of the reasons I started writing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WordPress Malware Removal for Developers &amp;amp; Site Owners&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The book is now available in &lt;strong&gt;Early Access on Leanpub&lt;/strong&gt;, with Chapter 1 available as a free sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I’m Writing This Book
&lt;/h2&gt;

&lt;p&gt;I’ve been working with WordPress sites since 2018 through freelance marketplaces, agencies, direct client projects, and my own company, 3Zero Digital.&lt;/p&gt;

&lt;p&gt;During that time, I’ve investigated and cleaned more than &lt;strong&gt;4,500 hacked WordPress sites&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The incidents were rarely as simple as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run a malware scanner → delete the infected file → change the password → done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Real cases are usually messier.&lt;/p&gt;

&lt;p&gt;I’ve worked on sites where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;desktop visitors saw a normal website while mobile visitors were redirected&lt;/li&gt;
&lt;li&gt;malware was stored in the WordPress database rather than an obvious PHP file&lt;/li&gt;
&lt;li&gt;administrators existed in the database but were hidden from the WordPress Users screen&lt;/li&gt;
&lt;li&gt;fake plugins were designed to look harmless&lt;/li&gt;
&lt;li&gt;SEO spam appeared in Google without being obvious inside WordPress&lt;/li&gt;
&lt;li&gt;malicious behavior came back after the first cleanup&lt;/li&gt;
&lt;li&gt;checkout pages loaded suspicious remote code&lt;/li&gt;
&lt;li&gt;the WordPress files looked clean while the problem existed at the hosting, DNS, or CDN layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That experience shaped the main idea behind the book:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Finding malware is not the same as understanding what happened.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Start With the Symptom, Not the Word “Malware”
&lt;/h2&gt;

&lt;p&gt;One of the first lessons in the book is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Hacked” is a conclusion. The symptom is what you actually observed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the site redirects&lt;/li&gt;
&lt;li&gt;the screen is blank&lt;/li&gt;
&lt;li&gt;Google shows pages you never created&lt;/li&gt;
&lt;li&gt;an administrator is missing&lt;/li&gt;
&lt;li&gt;checkout suddenly behaves differently&lt;/li&gt;
&lt;li&gt;a visitor gets a warning&lt;/li&gt;
&lt;li&gt;a deleted infection keeps returning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each symptom may have a security explanation.&lt;/p&gt;

&lt;p&gt;But many of them can also have ordinary explanations.&lt;/p&gt;

&lt;p&gt;A blank page could be caused by malicious code replacing &lt;code&gt;index.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or it could be caused by a PHP or theme mistake.&lt;/p&gt;

&lt;p&gt;The symptom alone does not decide which one is true.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence does.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Site Looking Normal Does Not Prove It Is Clean
&lt;/h2&gt;

&lt;p&gt;One of the real cases used in Chapter 1 involved a website that appeared completely normal from the owner's desktop.&lt;/p&gt;

&lt;p&gt;But a request using a mobile browser identity received a redirect to an external domain.&lt;/p&gt;

&lt;p&gt;The malicious rule was checking the visitor before deciding what response to send.&lt;/p&gt;

&lt;p&gt;That means checking the homepage once from your normal browser is a very weak security test.&lt;/p&gt;

&lt;p&gt;A compromised site may behave differently depending on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mobile vs desktop&lt;/li&gt;
&lt;li&gt;logged-in vs logged-out users&lt;/li&gt;
&lt;li&gt;first visit vs returning visit&lt;/li&gt;
&lt;li&gt;direct traffic vs Google traffic&lt;/li&gt;
&lt;li&gt;cookies&lt;/li&gt;
&lt;li&gt;referrer&lt;/li&gt;
&lt;li&gt;IP or geographic conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why WordPress malware investigation often requires looking beyond the WordPress dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Inside the Book
&lt;/h2&gt;

&lt;p&gt;The current plan includes 16 chapters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is the WordPress Site Actually Hacked?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How Does a WordPress Site Get Hacked?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What to Do Before Cleaning Malware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find What Else Is Infected&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove Malware from WordPress Files&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove Malware from the WordPress Database&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean the Hosting Account, Server, and DNS&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find and Remove WordPress Redirect Malware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove WordPress SEO Spam and Hacked URLs From Google&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find and Remove Fake WordPress Plugins and Themes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove Unwanted and Hidden WordPress Administrators&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find and Remove WooCommerce Credit-Card Skimming Malware&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosting and Domain Suspension Removal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blacklist Removal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop Malware from Coming Back&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure and Monitor a Recovered Site&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is to follow an incident from the first suspicious symptom through investigation, cleanup, recovery, and verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Not a Theory-Only Security Book
&lt;/h2&gt;

&lt;p&gt;The book is built from real WordPress malware cleanup cases and investigation experience.&lt;/p&gt;

&lt;p&gt;The examples include work across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress files&lt;/li&gt;
&lt;li&gt;plugins and themes&lt;/li&gt;
&lt;li&gt;database records&lt;/li&gt;
&lt;li&gt;users and capabilities&lt;/li&gt;
&lt;li&gt;browser source and Network requests&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;cron jobs&lt;/li&gt;
&lt;li&gt;hosting accounts&lt;/li&gt;
&lt;li&gt;DNS and CDN settings&lt;/li&gt;
&lt;li&gt;Google search results&lt;/li&gt;
&lt;li&gt;WooCommerce checkout behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also try to separate what the evidence proves from what it only suggests.&lt;/p&gt;

&lt;p&gt;For example, a suspicious loader contacting a remote server is evidence of suspicious behavior.&lt;/p&gt;

&lt;p&gt;It is not automatically proof that payment information was stolen.&lt;/p&gt;

&lt;p&gt;A malicious file proves that malicious code exists.&lt;/p&gt;

&lt;p&gt;It does not automatically prove how the attacker originally entered the site.&lt;/p&gt;

&lt;p&gt;Those distinctions matter during a real investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Early Access
&lt;/h2&gt;

&lt;p&gt;The book is being published progressively on Leanpub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chapter 1 is available now as a free sample.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Early Access readers buy once and receive the future chapters and updates to this edition as they are released.&lt;/p&gt;

&lt;p&gt;📖 &lt;strong&gt;Read Chapter 1 free or get Early Access:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leanpub.com/wordpressmalwareremovalfordeveloperssiteowners" rel="noopener noreferrer"&gt;https://leanpub.com/wordpressmalwareremovalfordeveloperssiteowners&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d also love to hear from WordPress developers, hosting professionals, security researchers, and site owners:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What part of WordPress malware cleanup causes the most confusion for you?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Why I’m Writing a Practical WordPress Malware Removal Book</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Fri, 14 Aug 2026 21:56:26 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/why-im-writing-a-practical-wordpress-malware-removal-book-5kk</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/why-im-writing-a-practical-wordpress-malware-removal-book-5kk</guid>
      <description>&lt;p&gt;When a WordPress site gets hacked, one of the first recommendations is usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run a malware scanner,&lt;/li&gt;
&lt;li&gt;reinstall WordPress,&lt;/li&gt;
&lt;li&gt;update plugins,&lt;/li&gt;
&lt;li&gt;change passwords,&lt;/li&gt;
&lt;li&gt;and install a security plugin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those steps can be useful.&lt;/p&gt;

&lt;p&gt;But after working on real compromised WordPress sites, I kept seeing incidents that did not fit neatly into that checklist.&lt;/p&gt;

&lt;p&gt;A site could look completely normal on desktop while mobile visitors were redirected to spam.&lt;/p&gt;

&lt;p&gt;A malware scanner could report nothing while malicious JavaScript was stored inside the WordPress database.&lt;/p&gt;

&lt;p&gt;The WordPress Users screen could show one administrator while another privileged account was hidden by malicious code.&lt;/p&gt;

&lt;p&gt;A clean-looking plugin could be legitimate software but still contain an unauthorized code snippet stored through its database settings.&lt;/p&gt;

&lt;p&gt;And sometimes the WordPress files were clean because the real problem was outside WordPress entirely—in the server, DNS, CDN, or hosting account.&lt;/p&gt;

&lt;p&gt;That is why I started writing &lt;strong&gt;WordPress Malware Removal for Developers &amp;amp; Site Owners&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is a practical, evidence-led field guide built around real malware cleanup cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Wanted to Write This Book
&lt;/h2&gt;

&lt;p&gt;There is already a huge amount of WordPress security information online.&lt;/p&gt;

&lt;p&gt;The problem is that a lot of malware-removal content stops at detection.&lt;/p&gt;

&lt;p&gt;You are shown suspicious files, common malware signatures, security plugins, or a list of folders to check.&lt;/p&gt;

&lt;p&gt;But in a real investigation, finding a suspicious file is often only the beginning.&lt;/p&gt;

&lt;p&gt;You still need to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this file actually malicious?&lt;/li&gt;
&lt;li&gt;What caused the behavior the visitor reported?&lt;/li&gt;
&lt;li&gt;Is the malware stored in a file or in the database?&lt;/li&gt;
&lt;li&gt;Why does the redirect appear only on mobile devices?&lt;/li&gt;
&lt;li&gt;Why did the malware return after it was deleted?&lt;/li&gt;
&lt;li&gt;Is an unfamiliar administrator actually unauthorized?&lt;/li&gt;
&lt;li&gt;Is a plugin fake, modified, or simply custom?&lt;/li&gt;
&lt;li&gt;Did a checkout script actually steal card data, or do we only have evidence that it &lt;em&gt;could&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;Did the attacker enter through WordPress at all?&lt;/li&gt;
&lt;li&gt;What else needs to be cleaned before the site can be trusted again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the questions I wanted the book to focus on.&lt;/p&gt;

&lt;p&gt;The goal is not to teach readers to search for scary-looking PHP functions.&lt;/p&gt;

&lt;p&gt;The goal is to teach them how to &lt;strong&gt;follow the evidence&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  WordPress Malware Removal Is More Than Cleaning Files
&lt;/h2&gt;

&lt;p&gt;One of the main ideas in the book is that a hacked WordPress site has several different layers.&lt;/p&gt;

&lt;p&gt;Malware may exist in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress core files,&lt;/li&gt;
&lt;li&gt;plugins,&lt;/li&gt;
&lt;li&gt;themes,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mu-plugins&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;uploaded or hidden PHP files,&lt;/li&gt;
&lt;li&gt;the WordPress database,&lt;/li&gt;
&lt;li&gt;user accounts and permissions,&lt;/li&gt;
&lt;li&gt;scheduled tasks,&lt;/li&gt;
&lt;li&gt;server configuration,&lt;/li&gt;
&lt;li&gt;hosting accounts,&lt;/li&gt;
&lt;li&gt;DNS records,&lt;/li&gt;
&lt;li&gt;CDN rules,&lt;/li&gt;
&lt;li&gt;or another website sharing the same hosting environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why simply deleting the first malicious file you find can fail.&lt;/p&gt;

&lt;p&gt;The file may only be the visible payload.&lt;/p&gt;

&lt;p&gt;Something else may still be able to recreate it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Cases Instead of Generic Malware Examples
&lt;/h2&gt;

&lt;p&gt;A large part of the book is based on real cleanup and research cases.&lt;/p&gt;

&lt;p&gt;The examples are used to explain how an investigation develops—not just to show screenshots of malicious code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional WordPress Redirect Malware
&lt;/h3&gt;

&lt;p&gt;In one case, the site owner could browse the website normally from a desktop computer.&lt;/p&gt;

&lt;p&gt;Visitors using phones were redirected elsewhere.&lt;/p&gt;

&lt;p&gt;The difference turned out to matter more than the first malware scan.&lt;/p&gt;

&lt;p&gt;The malicious rule checked the visitor's device before deciding whether to redirect them.&lt;/p&gt;

&lt;p&gt;That case became part of the chapter on finding and removing &lt;strong&gt;WordPress redirect malware&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Malware Hidden in the WordPress Database
&lt;/h3&gt;

&lt;p&gt;WordPress malware does not have to exist as a &lt;code&gt;.php&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Scripts, redirects, spam, malicious snippets, and other unauthorized values can be stored in normal WordPress database records.&lt;/p&gt;

&lt;p&gt;The book covers how to search the database, identify which WordPress feature owns a suspicious value, and decide whether it should be repaired or removed without destroying legitimate site data.&lt;/p&gt;




&lt;h3&gt;
  
  
  Hidden WordPress Administrators
&lt;/h3&gt;

&lt;p&gt;One investigation contained an administrator account that existed in the database but did not appear normally in the WordPress Dashboard.&lt;/p&gt;

&lt;p&gt;Malicious code altered the user query and displayed counts so the account was harder to notice.&lt;/p&gt;

&lt;p&gt;This is why one of the recurring lessons in the book is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not let one WordPress screen become your only source of truth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The hidden-administrator chapter follows the account through the database, the code hiding it, its permissions, sessions, and the cleanup process.&lt;/p&gt;




&lt;h3&gt;
  
  
  Fake Plugins and Hidden Backdoors
&lt;/h3&gt;

&lt;p&gt;Attackers do not always create files with obvious names.&lt;/p&gt;

&lt;p&gt;A malicious plugin may use a believable name, description, author, and version number.&lt;/p&gt;

&lt;p&gt;It may even remove itself from the normal Plugins screen.&lt;/p&gt;

&lt;p&gt;The book shows how to compare what WordPress displays with what actually exists in the filesystem, verify plugin identities against trusted sources, and investigate suspicious behavior before deleting anything.&lt;/p&gt;




&lt;h3&gt;
  
  
  Checkout Skimmers and Payment-Page Malware
&lt;/h3&gt;

&lt;p&gt;E-commerce compromises require extra care.&lt;/p&gt;

&lt;p&gt;A suspicious script appearing on a WooCommerce checkout page can have much more serious consequences than the same script appearing on an ordinary blog page.&lt;/p&gt;

&lt;p&gt;The book includes a real investigation involving a checkout-targeted remote JavaScript loader.&lt;/p&gt;

&lt;p&gt;But there is an important distinction:&lt;/p&gt;

&lt;p&gt;The evidence showed that remote code could run on the checkout page.&lt;/p&gt;

&lt;p&gt;The retained evidence did &lt;strong&gt;not&lt;/strong&gt; prove that credit-card data had actually been stolen.&lt;/p&gt;

&lt;p&gt;That difference matters.&lt;/p&gt;

&lt;p&gt;A security investigation should say what the evidence supports—not what makes the most dramatic headline.&lt;/p&gt;




&lt;h3&gt;
  
  
  SEO Spam and Hacked URLs
&lt;/h3&gt;

&lt;p&gt;Another common WordPress compromise appears through search engines rather than the visible website.&lt;/p&gt;

&lt;p&gt;A site owner may see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;casino pages,&lt;/li&gt;
&lt;li&gt;pharmaceutical keywords,&lt;/li&gt;
&lt;li&gt;Japanese product spam,&lt;/li&gt;
&lt;li&gt;unfamiliar indexed URLs,&lt;/li&gt;
&lt;li&gt;or Search Console traffic for topics completely unrelated to the business.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Removing the spam from WordPress is only part of the recovery.&lt;/p&gt;

&lt;p&gt;The book also looks at how those URLs remain visible in search engines and how to verify what the compromised site is still serving.&lt;/p&gt;




&lt;h3&gt;
  
  
  Server, DNS, and CDN Compromise
&lt;/h3&gt;

&lt;p&gt;Sometimes repeatedly scanning WordPress will never find the problem.&lt;/p&gt;

&lt;p&gt;In one type of incident, visitors may be reaching the wrong server because a DNS record was changed.&lt;/p&gt;

&lt;p&gt;A CDN rule, hosting redirect, server configuration, or compromised hosting account can also affect what visitors receive before WordPress has a chance to run.&lt;/p&gt;

&lt;p&gt;That is why the book does not treat WordPress as an isolated application.&lt;/p&gt;




&lt;h3&gt;
  
  
  Malware That Keeps Coming Back
&lt;/h3&gt;

&lt;p&gt;Reinfection is one of the most frustrating WordPress malware problems.&lt;/p&gt;

&lt;p&gt;A site gets cleaned.&lt;/p&gt;

&lt;p&gt;The malicious file disappears.&lt;/p&gt;

&lt;p&gt;Then it comes back.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly deleting the same payload, the investigation has to find what can restore it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;another malicious file,&lt;/li&gt;
&lt;li&gt;a hidden plugin,&lt;/li&gt;
&lt;li&gt;a scheduled task,&lt;/li&gt;
&lt;li&gt;a compromised account,&lt;/li&gt;
&lt;li&gt;another infected website,&lt;/li&gt;
&lt;li&gt;a database value,&lt;/li&gt;
&lt;li&gt;or something outside WordPress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The book treats recurrence as evidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Site Can Look Hacked Without Being Hacked
&lt;/h2&gt;

&lt;p&gt;The opposite lesson matters too.&lt;/p&gt;

&lt;p&gt;Not every WordPress error is malware.&lt;/p&gt;

&lt;p&gt;A blank page can come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a PHP error,&lt;/li&gt;
&lt;li&gt;a plugin conflict,&lt;/li&gt;
&lt;li&gt;a broken theme,&lt;/li&gt;
&lt;li&gt;a failed update,&lt;/li&gt;
&lt;li&gt;a memory problem,&lt;/li&gt;
&lt;li&gt;or a normal coding mistake.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In one real case, a WordPress site showed a white screen because of a problem in the active theme's &lt;code&gt;functions.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In another case, an almost identical blank site had a compromised root &lt;code&gt;index.php&lt;/code&gt; containing malicious loader behavior.&lt;/p&gt;

&lt;p&gt;Same symptom.&lt;/p&gt;

&lt;p&gt;Two completely different conclusions.&lt;/p&gt;

&lt;p&gt;That is why the first chapter of the book begins with a simple principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Start with the symptom, not the word “malware.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What the Book Covers
&lt;/h2&gt;

&lt;p&gt;The current book includes chapters on topics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;identifying whether a WordPress site is actually hacked,&lt;/li&gt;
&lt;li&gt;understanding how WordPress sites get compromised,&lt;/li&gt;
&lt;li&gt;preparing safely before malware cleanup,&lt;/li&gt;
&lt;li&gt;finding connected infections,&lt;/li&gt;
&lt;li&gt;cleaning WordPress core, plugins, and themes,&lt;/li&gt;
&lt;li&gt;removing malware from the WordPress database,&lt;/li&gt;
&lt;li&gt;cleaning users and WordPress state,&lt;/li&gt;
&lt;li&gt;investigating hosting, server, DNS, and CDN problems,&lt;/li&gt;
&lt;li&gt;finding and removing WordPress redirect malware,&lt;/li&gt;
&lt;li&gt;removing SEO spam and hacked URLs,&lt;/li&gt;
&lt;li&gt;detecting fake plugins, themes, and hidden backdoors,&lt;/li&gt;
&lt;li&gt;removing hidden WordPress administrators,&lt;/li&gt;
&lt;li&gt;investigating checkout skimmers and payment-page malware,&lt;/li&gt;
&lt;li&gt;and handling malware that returns after cleanup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More chapters are still being developed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who This Book Is For
&lt;/h2&gt;

&lt;p&gt;I am writing it primarily for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress developers,&lt;/li&gt;
&lt;li&gt;freelancers,&lt;/li&gt;
&lt;li&gt;site owners,&lt;/li&gt;
&lt;li&gt;agency teams,&lt;/li&gt;
&lt;li&gt;hosting and technical-support staff,&lt;/li&gt;
&lt;li&gt;and people who want to understand WordPress malware cleanup beyond automated scanners.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is not meant to turn every reader into a malware analyst overnight.&lt;/p&gt;

&lt;p&gt;Instead, I want readers to understand &lt;strong&gt;why&lt;/strong&gt; a cleanup step is being performed and what evidence should support it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Book Is Still Under Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WordPress Malware Removal for Developers &amp;amp; Site Owners&lt;/strong&gt; is currently an early-access work in progress.&lt;/p&gt;

&lt;p&gt;The first chapter is already available as a free preview:&lt;/p&gt;

&lt;h3&gt;
  
  
  📘 Read Chapter 1 Free
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Is the WordPress Site Actually Hacked?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.mdpabel.com/wordpress-malware-removal-book/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=book_overview" rel="noopener noreferrer"&gt;Read the free chapter and join the book waitlist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first chapter looks at mobile-only redirects, blank pages, browser evidence, scanners, hidden administrators, search-engine clues, logs, DNS, and the difference between a suspicious symptom and confirmed compromise.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Principle I Want the Book to Keep
&lt;/h2&gt;

&lt;p&gt;If I had to reduce the entire project to one idea, it would probably be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Finding malware is not the same as understanding the compromise.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A malicious file tells you that a malicious file exists.&lt;/p&gt;

&lt;p&gt;A database injection tells you that an unauthorized value was stored.&lt;/p&gt;

&lt;p&gt;A hidden administrator tells you that unauthorized access exists.&lt;/p&gt;

&lt;p&gt;None of those observations automatically proves how the attacker first entered, what else they changed, or whether they still have another way back in.&lt;/p&gt;

&lt;p&gt;That is the investigation the rest of the book is about.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;WordPress Malware Removal for Developers &amp;amp; Site Owners is currently under development. New chapters and case studies will be added as the manuscript progresses.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Check Whether a WordPress Plugin or Theme Version Is Vulnerable</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Tue, 04 Aug 2026 20:57:53 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/how-to-check-whether-a-wordpress-plugin-or-theme-version-is-vulnerable-37g9</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/how-to-check-whether-a-wordpress-plugin-or-theme-version-is-vulnerable-37g9</guid>
      <description>&lt;p&gt;A WordPress plugin may have published vulnerabilities, but that does not mean every version of the plugin is affected.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;installed version&lt;/strong&gt; matters.&lt;/p&gt;

&lt;p&gt;For example, a vulnerability may affect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All versions up to and including 2.8.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your website uses version &lt;code&gt;2.8.8&lt;/code&gt;, that specific vulnerability may already be patched.&lt;/p&gt;

&lt;p&gt;The problem is that many WordPress site owners do not know how to compare their installed versions with documented affected-version ranges.&lt;/p&gt;

&lt;p&gt;That is why I built the &lt;strong&gt;3Zero WordPress Vulnerability Checker&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the checker does
&lt;/h2&gt;

&lt;p&gt;The tool lets you check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress plugins&lt;/li&gt;
&lt;li&gt;WordPress themes&lt;/li&gt;
&lt;li&gt;WordPress core&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter the software name and your installed version.&lt;/p&gt;

&lt;p&gt;The checker compares it with documented affected-version ranges in our synchronized vulnerability database.&lt;/p&gt;

&lt;p&gt;The system currently includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;23 years of WordPress vulnerability data&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Vulnerability records covering &lt;strong&gt;18,044 WordPress plugins and themes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;WordPress core vulnerability records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing is installed on your website.&lt;/p&gt;

&lt;p&gt;The tool does not connect to your WordPress dashboard or scan your server. You only enter the software name and version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the checker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.3zerodigital.com/research/wordpress-vulnerability-checker/" rel="noopener noreferrer"&gt;Check your WordPress plugin, theme, or core version&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to find your installed version
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Plugin version
&lt;/h3&gt;

&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress Dashboard
→ Plugins
→ Installed Plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installed version normally appears beside the plugin name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Theme version
&lt;/h3&gt;

&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress Dashboard
→ Appearance
→ Themes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select the active or installed theme to view its version.&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress core version
&lt;/h3&gt;

&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress Dashboard
→ Dashboard
→ Updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the WordPress version in the &lt;strong&gt;At a Glance&lt;/strong&gt; dashboard panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an affected-version match means
&lt;/h2&gt;

&lt;p&gt;A match means the version you entered falls inside a documented affected-version range.&lt;/p&gt;

&lt;p&gt;The result may show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vulnerability title&lt;/li&gt;
&lt;li&gt;Severity&lt;/li&gt;
&lt;li&gt;CVSS score&lt;/li&gt;
&lt;li&gt;Affected version range&lt;/li&gt;
&lt;li&gt;Known patched version&lt;/li&gt;
&lt;li&gt;Publication date&lt;/li&gt;
&lt;li&gt;Original source record&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Installed version: 2.8.7
Affected-version match: Yes
Known patched version: 2.8.8 or later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A vulnerable version does not mean the website is hacked
&lt;/h2&gt;

&lt;p&gt;This is an important difference.&lt;/p&gt;

&lt;p&gt;A vulnerable version shows that a documented security weakness may have existed in the installed software.&lt;/p&gt;

&lt;p&gt;It does not prove that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An attacker used the vulnerability&lt;/li&gt;
&lt;li&gt;Malware is present&lt;/li&gt;
&lt;li&gt;The website was compromised&lt;/li&gt;
&lt;li&gt;The vulnerable component was the original entry point&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confirming an actual incident requires evidence from the website, database, server, user accounts, and available logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do after finding a match?
&lt;/h2&gt;

&lt;p&gt;First, check whether a patched version is available.&lt;/p&gt;

&lt;p&gt;Create a reliable backup and update the affected plugin, theme, or WordPress core.&lt;/p&gt;

&lt;p&gt;Depending on the vulnerability type and the website's behaviour, you may also need to review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unknown administrator accounts&lt;/li&gt;
&lt;li&gt;Recent user-role changes&lt;/li&gt;
&lt;li&gt;Suspicious or recently modified files&lt;/li&gt;
&lt;li&gt;Unexpected scheduled tasks&lt;/li&gt;
&lt;li&gt;Modified database values&lt;/li&gt;
&lt;li&gt;Active login sessions&lt;/li&gt;
&lt;li&gt;Unfamiliar redirects&lt;/li&gt;
&lt;li&gt;Injected pages or scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Updating closes the known vulnerability.&lt;/p&gt;

&lt;p&gt;However, it does not automatically remove changes an attacker may have made before the update.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does “no match found” mean?
&lt;/h2&gt;

&lt;p&gt;A no-match result means the entered version did not match an affected-version range currently available in the synchronized dataset.&lt;/p&gt;

&lt;p&gt;It does not guarantee that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The software has no security issues&lt;/li&gt;
&lt;li&gt;The website is completely secure&lt;/li&gt;
&lt;li&gt;The website contains no malware&lt;/li&gt;
&lt;li&gt;Every possible vulnerability has been documented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool answers one specific question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this installed version match a documented affected-version range?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a version checker, not a complete malware scanner or website security audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who can use this tool?
&lt;/h2&gt;

&lt;p&gt;The checker may be useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress site owners&lt;/li&gt;
&lt;li&gt;Freelance developers&lt;/li&gt;
&lt;li&gt;Website maintenance providers&lt;/li&gt;
&lt;li&gt;WordPress agencies&lt;/li&gt;
&lt;li&gt;Hosting support teams&lt;/li&gt;
&lt;li&gt;Security professionals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It can help you identify which installed software versions may need faster attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your WordPress software
&lt;/h2&gt;

&lt;p&gt;Use the free checker here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.3zerodigital.com/research/wordpress-vulnerability-checker/" rel="noopener noreferrer"&gt;3Zero WordPress Vulnerability Checker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vulnerability data:&lt;/strong&gt; Wordfence Intelligence&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Version matching, presentation, and analysis:&lt;/strong&gt; 3Zero Digital&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>How to Investigate WordPress ?p=, ?q=, and ?a= SEO Spam URLs</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Mon, 20 Jul 2026 08:15:34 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/how-to-investigate-wordpress-p-q-and-a-seo-spam-urls-1jgc</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/how-to-investigate-wordpress-p-q-and-a-seo-spam-urls-1jgc</guid>
      <description>&lt;p&gt;During a WordPress SEO spam investigation, I found Google requesting URLs like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/?p=23932
https://example.com/?q=98237492
https://example.com/?a=739284729
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, they look similar: a short query parameter followed by a large number.&lt;/p&gt;

&lt;p&gt;But they should not all be treated the same way.&lt;/p&gt;

&lt;p&gt;The most dangerous mistake would be to create a rule that blocks every single-letter query parameter.&lt;/p&gt;

&lt;p&gt;That could break:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real WordPress posts&lt;/li&gt;
&lt;li&gt;Internal searches&lt;/li&gt;
&lt;li&gt;Plugin filters&lt;/li&gt;
&lt;li&gt;Tracking parameters&lt;/li&gt;
&lt;li&gt;Form submissions&lt;/li&gt;
&lt;li&gt;Application features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a larger cleanup involving approximately 242,000 unwanted URL variations, I had to separate legitimate WordPress behaviour from malicious URL patterns.&lt;/p&gt;

&lt;p&gt;The full incident is documented in my &lt;a href="https://www.mdpabel.com/case-studies/remove-spam-urls-from-google/" rel="noopener noreferrer"&gt;spam URL removal case study&lt;/a&gt;. This article focuses on query-string analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a query string?
&lt;/h2&gt;

&lt;p&gt;A query string is the part of a URL following the question mark.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/?p=123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query string is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p=123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple parameters can be separated with an ampersand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/?page=2&amp;amp;sort=price
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query strings are normal.&lt;/p&gt;

&lt;p&gt;Their existence does not mean a website is hacked.&lt;/p&gt;

&lt;p&gt;The issue begins when malware generates large numbers of meaningless or spam-producing variations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;?p=&lt;/code&gt; can be legitimate
&lt;/h2&gt;

&lt;p&gt;WordPress uses the &lt;code&gt;p&lt;/code&gt; parameter to request a post by its database ID.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/?p=123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If post ID &lt;code&gt;123&lt;/code&gt; exists, WordPress may redirect the request to its normal permalink:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/example-post/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, this rule would be unsafe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)p=[0-9]+(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would block every WordPress post query URL.&lt;/p&gt;

&lt;p&gt;Even when the site uses pretty permalinks, old links or external sites may still reference the &lt;code&gt;?p=&lt;/code&gt; version.&lt;/p&gt;

&lt;h2&gt;
  
  
  How &lt;code&gt;?p=&lt;/code&gt; was abused in the incident
&lt;/h2&gt;

&lt;p&gt;The hacked website produced many URLs in a narrow numerical range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?p=23981
/?p=23932
/?p=23919
/?p=23916
/?p=23860
/?p=23795
/?p=23783
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before blocking the range, I checked whether those IDs belonged to real posts.&lt;/p&gt;

&lt;p&gt;A database query can help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;23000&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;24999&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If legitimate records exist, a broad range rule may not be appropriate.&lt;/p&gt;

&lt;p&gt;You may need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exclude known IDs&lt;/li&gt;
&lt;li&gt;Match another part of the request&lt;/li&gt;
&lt;li&gt;Handle the response in PHP&lt;/li&gt;
&lt;li&gt;Use a smaller numerical range&lt;/li&gt;
&lt;li&gt;Avoid blocking the pattern entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;?q=&lt;/code&gt; may also be legitimate
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;q&lt;/code&gt; parameter is not WordPress’s default search parameter, but plugins and custom applications may use it.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search plugins&lt;/li&gt;
&lt;li&gt;Product filters&lt;/li&gt;
&lt;li&gt;Documentation systems&lt;/li&gt;
&lt;li&gt;External integrations&lt;/li&gt;
&lt;li&gt;Analytics tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Never assume &lt;code&gt;?q=&lt;/code&gt; is malicious simply because it appears in spam examples.&lt;/p&gt;

&lt;p&gt;Verify:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the legitimate site use it?&lt;/li&gt;
&lt;li&gt;What response does a normal &lt;code&gt;?q=&lt;/code&gt; request produce?&lt;/li&gt;
&lt;li&gt;Is the suspicious value always numerical?&lt;/li&gt;
&lt;li&gt;Does the spam use a consistent length or range?&lt;/li&gt;
&lt;li&gt;Do logs show a common user agent or referrer?&lt;/li&gt;
&lt;li&gt;Does the pattern appear in Google Search Console?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;?a=&lt;/code&gt; and other single-letter parameters
&lt;/h2&gt;

&lt;p&gt;Attackers sometimes generate many variants by changing the parameter name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?a=839472938
/?b=739284729
/?x=193847293
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tempting firewall rule is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)[a-z]=[0-9]+(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is extremely broad.&lt;/p&gt;

&lt;p&gt;It may block legitimate features that use parameters like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;?s=wordpress
?p=123
?m=202607
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pattern matching should be evidence-driven.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collect URLs before blocking them
&lt;/h2&gt;

&lt;p&gt;I combined URLs from several sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Search Console Pages report&lt;/li&gt;
&lt;li&gt;Search performance exports&lt;/li&gt;
&lt;li&gt;Server access logs&lt;/li&gt;
&lt;li&gt;Google search results&lt;/li&gt;
&lt;li&gt;Malicious sitemap files&lt;/li&gt;
&lt;li&gt;Security scanner reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The URLs were saved in a CSV file for analysis.&lt;/p&gt;

&lt;p&gt;A simple CSV might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URL
https://example.com/?a=839472938
https://example.com/?q=739284729
https://example.com/?p=23932
https://example.com/real-page/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Filter the domain with Python
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;INPUT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urls.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain-urls.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;SITE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INPUT_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The CSV must contain a column named URL.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;domain_urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SITE_URL&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;domain_urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saved &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain_urls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; URLs to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extract query parameters
&lt;/h2&gt;

&lt;p&gt;Python’s standard library can separate parameter names and values safely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_qs&lt;/span&gt;

&lt;span class="n"&gt;INPUT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain-urls.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query-parameters.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INPUT_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_qs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keep_blank_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parameter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;numeric&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saved &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; parameter rows to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it easier to answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which parameter appears most often?&lt;/li&gt;
&lt;li&gt;Are the values always numbers?&lt;/li&gt;
&lt;li&gt;What is the usual value length?&lt;/li&gt;
&lt;li&gt;Are the values concentrated in one range?&lt;/li&gt;
&lt;li&gt;Do different parameters generate the same content?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Count the patterns
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query-parameters.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parameter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;numeric&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ascending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parameter  numeric  count
a          True     8421
q          True     6138
p          True     1820
s          False      42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not prove that &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;q&lt;/code&gt;, or &lt;code&gt;p&lt;/code&gt; are malicious.&lt;/p&gt;

&lt;p&gt;It identifies where to investigate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the server response
&lt;/h2&gt;

&lt;p&gt;Test several examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?a=839472938"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?q=739284729"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?p=23932"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check whether they return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;200&lt;/li&gt;
&lt;li&gt;301&lt;/li&gt;
&lt;li&gt;302&lt;/li&gt;
&lt;li&gt;404&lt;/li&gt;
&lt;li&gt;410&lt;/li&gt;
&lt;li&gt;500&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A hacked URL returning 200 is especially important because Google may treat it as a real page.&lt;/p&gt;

&lt;p&gt;Also inspect the body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?a=839472938"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 40
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare normal and suspicious requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check access logs
&lt;/h2&gt;

&lt;p&gt;Useful questions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is Googlebot requesting the URLs?&lt;/li&gt;
&lt;li&gt;Are other bots requesting the same patterns?&lt;/li&gt;
&lt;li&gt;How often are they requested?&lt;/li&gt;
&lt;li&gt;Do they reach &lt;code&gt;index.php&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Are they producing expensive application responses?&lt;/li&gt;
&lt;li&gt;Did the requests continue after malware removal?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example Apache log searches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'\?(a|q|p)=[0-9]+'&lt;/span&gt; access.log | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Count matching requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Ec&lt;/span&gt; &lt;span class="s1"&gt;'\?(a|q|p)=[0-9]+'&lt;/span&gt; access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View the most common URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'\?(a|q|p)=[0-9]+'&lt;/span&gt; access.log &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $7}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-nr&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log formats differ, so the URL may not always be in field seven.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the narrowest possible rule
&lt;/h2&gt;

&lt;p&gt;Suppose the investigation confirms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; are not used legitimately&lt;/li&gt;
&lt;li&gt;Their spam values always contain at least eight digits&lt;/li&gt;
&lt;li&gt;Requests should never return content again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A narrow Apache rule could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="sr"&gt; mod_rewrite.c&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;

&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)(?:a|q)=[0-9]{8,}(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns 410 Gone.&lt;/p&gt;

&lt;p&gt;It does not block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;?s=wordpress
?p=123
?q=shoes
?a=42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whether those exclusions are correct depends on the actual website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle a confirmed &lt;code&gt;?p=&lt;/code&gt; range separately
&lt;/h2&gt;

&lt;p&gt;If a numerical range has been verified as malicious and contains no legitimate WordPress records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example range only: p=23000 through p=24999&lt;/span&gt;
&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)p=2[3-4][0-9]{3}(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is safer than blocking every post ID, but it still needs testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test both malicious and legitimate cases
&lt;/h2&gt;

&lt;p&gt;Spam tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?a=12345678"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?q=987654321"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?p=23932"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Legitimate tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?s=security"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?p=123"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/wp-login.php"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful spam response should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/2 410
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The legitimate URLs should keep their expected behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why server-level handling matters
&lt;/h2&gt;

&lt;p&gt;If WordPress handles every request, even a missing page can require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP startup&lt;/li&gt;
&lt;li&gt;WordPress bootstrap&lt;/li&gt;
&lt;li&gt;Plugin loading&lt;/li&gt;
&lt;li&gt;Database queries&lt;/li&gt;
&lt;li&gt;Theme rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A narrow Apache rule can stop confirmed spam requests earlier.&lt;/p&gt;

&lt;p&gt;That reduces unnecessary application work while also providing a clear status to search engines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not treat query patterns as malware signatures
&lt;/h2&gt;

&lt;p&gt;A URL such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?q=12345678
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is not automatically proof of malware.&lt;/p&gt;

&lt;p&gt;Context matters.&lt;/p&gt;

&lt;p&gt;The evidence should include some combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unwanted content&lt;/li&gt;
&lt;li&gt;Unexpected HTTP 200 responses&lt;/li&gt;
&lt;li&gt;Search Console impressions&lt;/li&gt;
&lt;li&gt;Large-scale repeated patterns&lt;/li&gt;
&lt;li&gt;Malicious files or database content&lt;/li&gt;
&lt;li&gt;Access-log activity&lt;/li&gt;
&lt;li&gt;Unknown sitemap entries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule should be the result of the investigation, not the beginning of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final checklist
&lt;/h2&gt;

&lt;p&gt;Before blocking query-string spam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export representative URLs&lt;/li&gt;
&lt;li&gt;Group them by parameter and structure&lt;/li&gt;
&lt;li&gt;Confirm whether the parameter is used legitimately&lt;/li&gt;
&lt;li&gt;Check database IDs for &lt;code&gt;?p=&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Test current HTTP responses&lt;/li&gt;
&lt;li&gt;Inspect server logs&lt;/li&gt;
&lt;li&gt;Create the narrowest rule possible&lt;/li&gt;
&lt;li&gt;Test legitimate pages after deployment&lt;/li&gt;
&lt;li&gt;Monitor 410 responses&lt;/li&gt;
&lt;li&gt;Keep the patterns out of the canonical sitemap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The larger lesson is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not block thousands of URLs individually when a small number of verified patterns explains them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The full cleanup—including malware removal, Google Search Console requests, 410 handling, and sitemap recovery—is available in my &lt;a href="https://www.mdpabel.com/case-studies/remove-spam-urls-from-google/" rel="noopener noreferrer"&gt;complete WordPress SEO spam case study&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>python</category>
      <category>apache</category>
    </item>
    <item>
      <title>Why Cleaning WordPress Malware Does Not Remove Spam Pages From Google</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Mon, 20 Jul 2026 08:15:13 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/why-cleaning-wordpress-malware-does-not-remove-spam-pages-from-google-5cd2</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/why-cleaning-wordpress-malware-does-not-remove-spam-pages-from-google-5cd2</guid>
      <description>&lt;p&gt;A website can be malware-free today while Google continues showing hacked pages for weeks or months.&lt;/p&gt;

&lt;p&gt;This confuses many website owners.&lt;/p&gt;

&lt;p&gt;They clean the WordPress installation, run another malware scan, open the homepage, and see that everything looks normal.&lt;/p&gt;

&lt;p&gt;Then they search Google and find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Japanese product titles&lt;/li&gt;
&lt;li&gt;Gambling pages&lt;/li&gt;
&lt;li&gt;Pharmaceutical keywords&lt;/li&gt;
&lt;li&gt;URLs they never created&lt;/li&gt;
&lt;li&gt;Content that is not visible in WordPress&lt;/li&gt;
&lt;li&gt;Random parameters such as &lt;code&gt;?p=23932&lt;/code&gt; or &lt;code&gt;?q=98237492&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The natural reaction is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the malware is gone, why is Google still showing the spam?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because malware cleanup and Google index cleanup are two different processes.&lt;/p&gt;

&lt;p&gt;I recently handled a case involving approximately 242,000 unwanted URL variations. The complete investigation is available in my &lt;a href="https://www.mdpabel.com/case-studies/remove-spam-urls-from-google/" rel="noopener noreferrer"&gt;WordPress SEO spam recovery case study&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is the most important lesson from that cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem one: the hacked website
&lt;/h2&gt;

&lt;p&gt;The first problem exists on the server.&lt;/p&gt;

&lt;p&gt;The attacker may have added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Malicious PHP files&lt;/li&gt;
&lt;li&gt;Backdoors&lt;/li&gt;
&lt;li&gt;Fake plugins&lt;/li&gt;
&lt;li&gt;Hidden administrator users&lt;/li&gt;
&lt;li&gt;Database injections&lt;/li&gt;
&lt;li&gt;Modified &lt;code&gt;.htaccess&lt;/code&gt; rules&lt;/li&gt;
&lt;li&gt;Malicious cron events&lt;/li&gt;
&lt;li&gt;PHP files inside the uploads directory&lt;/li&gt;
&lt;li&gt;Scripts that fetch spam from an external server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the active security incident.&lt;/p&gt;

&lt;p&gt;Until it is fixed, the website may continue producing new spam URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem two: Google already knows the URLs
&lt;/h2&gt;

&lt;p&gt;The second problem exists in Google’s crawl and index systems.&lt;/p&gt;

&lt;p&gt;Google may have already discovered the hacked URLs through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal links created by malware&lt;/li&gt;
&lt;li&gt;Malicious XML sitemaps&lt;/li&gt;
&lt;li&gt;External spam links&lt;/li&gt;
&lt;li&gt;Generated archive pages&lt;/li&gt;
&lt;li&gt;Search-engine cloaking&lt;/li&gt;
&lt;li&gt;Previous crawls of the infected site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cleaning the malicious code does not erase Google’s memory of those URLs.&lt;/p&gt;

&lt;p&gt;Google needs to revisit them and receive an appropriate signal.&lt;/p&gt;

&lt;p&gt;That signal may be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;410 Gone&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A legitimate redirect to a relevant replacement&lt;/li&gt;
&lt;li&gt;A valid &lt;code&gt;noindex&lt;/code&gt; response in cases where the page still exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For completely fake hacked URLs, I normally prefer 404 or 410 rather than redirecting everything to the homepage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the website can look normal
&lt;/h2&gt;

&lt;p&gt;SEO spam often does not affect every visitor equally.&lt;/p&gt;

&lt;p&gt;Malicious code may change its response based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User agent&lt;/li&gt;
&lt;li&gt;Referrer&lt;/li&gt;
&lt;li&gt;Cookie&lt;/li&gt;
&lt;li&gt;IP address&lt;/li&gt;
&lt;li&gt;Device&lt;/li&gt;
&lt;li&gt;Search-engine crawler&lt;/li&gt;
&lt;li&gt;Requested URL pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the owner may open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and see the real homepage.&lt;/p&gt;

&lt;p&gt;Googlebot may request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/jp/fake-product/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and receive a generated product page.&lt;/p&gt;

&lt;p&gt;The owner may never see that route inside WordPress admin because the page does not exist as a normal post.&lt;/p&gt;

&lt;p&gt;It may be dynamically generated by PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fake product pages appear
&lt;/h2&gt;

&lt;p&gt;Japanese keyword hacks commonly promote products through compromised domains.&lt;/p&gt;

&lt;p&gt;A hacked result may contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Japanese product names&lt;/li&gt;
&lt;li&gt;Prices&lt;/li&gt;
&lt;li&gt;Review text&lt;/li&gt;
&lt;li&gt;Shopping descriptions&lt;/li&gt;
&lt;li&gt;Product structured data&lt;/li&gt;
&lt;li&gt;Unrelated images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The website may not sell any of those products.&lt;/p&gt;

&lt;p&gt;The attacker is borrowing the domain’s reputation and crawl history.&lt;/p&gt;

&lt;p&gt;This is why searching only the WordPress Posts screen is not a complete investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: stop new spam from being created
&lt;/h2&gt;

&lt;p&gt;I always begin with the server and application.&lt;/p&gt;

&lt;p&gt;The investigation normally includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comparing WordPress core files with clean originals&lt;/li&gt;
&lt;li&gt;Checking plugins, themes, and must-use plugins&lt;/li&gt;
&lt;li&gt;Inspecting root and nested &lt;code&gt;.htaccess&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Searching the database&lt;/li&gt;
&lt;li&gt;Reviewing WordPress administrators&lt;/li&gt;
&lt;li&gt;Checking scheduled tasks&lt;/li&gt;
&lt;li&gt;Examining access logs&lt;/li&gt;
&lt;li&gt;Rotating compromised credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The site should not be considered clean simply because one scanner reports no malware.&lt;/p&gt;

&lt;p&gt;Scanners are helpful, but manual investigation is often needed to find persistence mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: collect examples of the hacked URLs
&lt;/h2&gt;

&lt;p&gt;After stopping the infection, I collect URLs from multiple sources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google searches
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;site:example.com
site:example.com inurl:jp
site:example.com filetype:html
site:example.com casino
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These searches do not provide an exact index count, but they can reveal visible patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google Search Console
&lt;/h3&gt;

&lt;p&gt;Useful reports include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indexing → Pages&lt;/li&gt;
&lt;li&gt;Performance → Search results&lt;/li&gt;
&lt;li&gt;Security Issues&lt;/li&gt;
&lt;li&gt;Manual Actions&lt;/li&gt;
&lt;li&gt;Sitemaps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server logs
&lt;/h3&gt;

&lt;p&gt;Logs can reveal URLs that bots continue requesting even when they no longer appear prominently in Search Console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search Analytics API
&lt;/h3&gt;

&lt;p&gt;The API can help export pages and queries that received impressions.&lt;/p&gt;

&lt;p&gt;It is useful performance data, but it should not be treated as a complete list of every URL Google knows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: look for patterns, not individual URLs
&lt;/h2&gt;

&lt;p&gt;The incident I investigated included patterns like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?a=839472938
/?q=739284729
/?p=23932
/pages/random-product.html
/jp/fake-category/
/products/23860
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Removing these one by one would not scale.&lt;/p&gt;

&lt;p&gt;Instead, I grouped them by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Directory&lt;/li&gt;
&lt;li&gt;Query parameter&lt;/li&gt;
&lt;li&gt;Numerical range&lt;/li&gt;
&lt;li&gt;File extension&lt;/li&gt;
&lt;li&gt;Spam keyword&lt;/li&gt;
&lt;li&gt;URL structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few repeatable patterns explained a large part of the 242,000 URL variations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: return a real removal status
&lt;/h2&gt;

&lt;p&gt;Once a pattern was confirmed to be malicious, the server returned 410 Gone before WordPress loaded.&lt;/p&gt;

&lt;p&gt;A simplified Apache example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;ErrorDocument&lt;/span&gt; 410 "&amp;lt;h1&amp;gt;410 Gone&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;This spam &lt;span class="ss"&gt;URL&lt;/span&gt; has been permanently removed.&amp;lt;/p&amp;gt;"

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="sr"&gt; mod_rewrite.c&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;

&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)(?:a|q)=[0-9]{5,}(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]

&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^(?:pages|jp)(?:/|$) - [G,L,NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^products/[0-9]+(?:/|$) - [G,L,NC]

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These rules are examples, not universal signatures.&lt;/p&gt;

&lt;p&gt;A rule must be based on patterns verified on the affected site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I did not redirect everything to the homepage
&lt;/h2&gt;

&lt;p&gt;A fake Japanese product page does not have a meaningful replacement on the website.&lt;/p&gt;

&lt;p&gt;Redirecting it to the homepage would communicate that the unrelated resource moved there.&lt;/p&gt;

&lt;p&gt;It may also result in soft-404 behaviour.&lt;/p&gt;

&lt;p&gt;My general decision process is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relevant replacement exists:&lt;/strong&gt; use 301&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource never existed or no longer exists:&lt;/strong&gt; use 404&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmed malicious resource deliberately removed:&lt;/strong&gt; 410 can be appropriate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page still exists but should not appear in search:&lt;/strong&gt; consider noindex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The response should match what actually happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: use Search Console removals carefully
&lt;/h2&gt;

&lt;p&gt;Google Search Console can temporarily hide urgent results.&lt;/p&gt;

&lt;p&gt;For a single URL, submit a specific request.&lt;/p&gt;

&lt;p&gt;For an entirely malicious directory, use:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remove all URLs with this prefix&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/jp/
https://example.com/pages/
https://example.com/products/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not submit a broad prefix if it includes legitimate pages.&lt;/p&gt;

&lt;p&gt;Search Console removal is temporary. It does not replace the server response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: submit a clean sitemap
&lt;/h2&gt;

&lt;p&gt;After the cleanup, the normal sitemap should contain only URLs that should appear in Google.&lt;/p&gt;

&lt;p&gt;That generally means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Canonical URLs&lt;/li&gt;
&lt;li&gt;HTTP 200 pages&lt;/li&gt;
&lt;li&gt;Real content&lt;/li&gt;
&lt;li&gt;Preferred HTTPS versions&lt;/li&gt;
&lt;li&gt;Preferred hostname versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It should not contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hacked URLs&lt;/li&gt;
&lt;li&gt;Redirects&lt;/li&gt;
&lt;li&gt;404 pages&lt;/li&gt;
&lt;li&gt;410 pages&lt;/li&gt;
&lt;li&gt;Internal search pages&lt;/li&gt;
&lt;li&gt;Random query parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The clean sitemap helps Google focus on the legitimate website structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: allow Google to recrawl the spam URLs
&lt;/h2&gt;

&lt;p&gt;It can be tempting to block the hacked paths in &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That can be counterproductive if Google needs to see their 404 or 410 response.&lt;/p&gt;

&lt;p&gt;Googlebot should be able to request the URL and confirm that it is gone.&lt;/p&gt;

&lt;p&gt;The spam URLs should be removed from the legitimate sitemap, but not hidden from the removal response.&lt;/p&gt;

&lt;h2&gt;
  
  
  How long does recovery take?
&lt;/h2&gt;

&lt;p&gt;The malware cleanup and the Google cleanup do not follow the same timeline.&lt;/p&gt;

&lt;p&gt;The security work may be completed quickly.&lt;/p&gt;

&lt;p&gt;Google still needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Revisit the affected URLs&lt;/li&gt;
&lt;li&gt;Observe the new status&lt;/li&gt;
&lt;li&gt;Process the changes&lt;/li&gt;
&lt;li&gt;Update search results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The time varies based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of URLs&lt;/li&gt;
&lt;li&gt;Crawl frequency&lt;/li&gt;
&lt;li&gt;Internal and external links&lt;/li&gt;
&lt;li&gt;Sitemap history&lt;/li&gt;
&lt;li&gt;Server stability&lt;/li&gt;
&lt;li&gt;Whether new spam continues appearing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why I avoid promising that hundreds of thousands of URLs will disappear immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signs that the recovery is working
&lt;/h2&gt;

&lt;p&gt;I monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declining spam-query impressions&lt;/li&gt;
&lt;li&gt;More hacked URLs reporting 404 or 410&lt;/li&gt;
&lt;li&gt;Correct titles returning&lt;/li&gt;
&lt;li&gt;Legitimate pages recovering visibility&lt;/li&gt;
&lt;li&gt;Googlebot requesting the old spam paths&lt;/li&gt;
&lt;li&gt;No new spam patterns&lt;/li&gt;
&lt;li&gt;No returning malicious files&lt;/li&gt;
&lt;li&gt;No new unauthorized users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An increase in reported 404 or 410 URLs is not always bad after a hack.&lt;/p&gt;

&lt;p&gt;It may show that Google is revisiting the malicious URLs and receiving the intended response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;A successful WordPress SEO spam cleanup has two parts:&lt;/p&gt;

&lt;h3&gt;
  
  
  Security recovery
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Remove malware&lt;/li&gt;
&lt;li&gt;Remove backdoors&lt;/li&gt;
&lt;li&gt;Close the entry point&lt;/li&gt;
&lt;li&gt;Secure accounts&lt;/li&gt;
&lt;li&gt;Stop new URLs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Search recovery
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Collect unwanted URLs&lt;/li&gt;
&lt;li&gt;Identify patterns&lt;/li&gt;
&lt;li&gt;Return 404 or 410&lt;/li&gt;
&lt;li&gt;Use temporary removals when needed&lt;/li&gt;
&lt;li&gt;Submit a clean sitemap&lt;/li&gt;
&lt;li&gt;Monitor recrawling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stopping after the malware scan leaves half the job unfinished.&lt;/p&gt;

&lt;p&gt;The full investigation—including the URL patterns, &lt;code&gt;.htaccess&lt;/code&gt; firewall, Search Console removals, and sitemap strategy—is documented in my &lt;a href="https://www.mdpabel.com/case-studies/remove-spam-urls-from-google/" rel="noopener noreferrer"&gt;complete 242,000-URL recovery case study&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Used 410 Gone Rules to Remove WordPress SEO Spam URLs</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Mon, 20 Jul 2026 08:14:42 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/how-i-used-410-gone-rules-to-remove-wordpress-seo-spam-urls-2gk3</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/how-i-used-410-gone-rules-to-remove-wordpress-seo-spam-urls-2gk3</guid>
      <description>&lt;p&gt;A hacked WordPress website can be cleaned in a few hours, but the damage left in Google may remain much longer.&lt;/p&gt;

&lt;p&gt;In one cleanup, the website looked normal when opened directly. Google Search showed something completely different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Japanese product pages&lt;/li&gt;
&lt;li&gt;Gambling-related URLs&lt;/li&gt;
&lt;li&gt;Random numerical query parameters&lt;/li&gt;
&lt;li&gt;Fake &lt;code&gt;.html&lt;/code&gt; pages&lt;/li&gt;
&lt;li&gt;Content the website owner had never published&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Approximately 242,000 unwanted URL variations were associated with the incident.&lt;/p&gt;

&lt;p&gt;Removing the malware stopped new pages from being generated, but Google already knew about many of the hacked URLs.&lt;/p&gt;

&lt;p&gt;That created a second problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do you permanently retire thousands of generated URLs without loading WordPress for every request?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My solution was to identify repeatable URL patterns and return &lt;strong&gt;410 Gone&lt;/strong&gt; at the Apache level.&lt;/p&gt;

&lt;p&gt;I documented the complete malware and Google index recovery in this &lt;a href="https://www.mdpabel.com/case-studies/remove-spam-urls-from-google/" rel="noopener noreferrer"&gt;WordPress SEO spam cleanup case study&lt;/a&gt;. This article focuses specifically on the server-side implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why malware removal was not enough
&lt;/h2&gt;

&lt;p&gt;Suppose malware creates URLs like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/?a=839472938
https://example.com/?q=739284729
https://example.com/pages/fake-product.html
https://example.com/jp/random-product/
https://example.com/products/23932
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the malicious PHP is removed, those URLs may stop displaying spam.&lt;/p&gt;

&lt;p&gt;But Googlebot, scrapers, security scanners, and other bots may continue requesting them for months.&lt;/p&gt;

&lt;p&gt;Without a server-level rule, every request might still:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start PHP&lt;/li&gt;
&lt;li&gt;Load WordPress core&lt;/li&gt;
&lt;li&gt;Load active plugins&lt;/li&gt;
&lt;li&gt;Connect to MySQL&lt;/li&gt;
&lt;li&gt;Run database queries&lt;/li&gt;
&lt;li&gt;Load the theme&lt;/li&gt;
&lt;li&gt;Generate a full 404 page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is unnecessary work for URLs that should never exist again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I chose 410 Gone
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;404 Not Found&lt;/code&gt; response means the server cannot find the requested resource.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;410 Gone&lt;/code&gt; response means the resource was deliberately removed and is not expected to return.&lt;/p&gt;

&lt;p&gt;Both statuses can eventually remove a URL from Google. I used 410 because these URLs were confirmed hacked resources that had no legitimate replacement.&lt;/p&gt;

&lt;p&gt;The important requirements were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The URL must not return HTTP 200&lt;/li&gt;
&lt;li&gt;The response must not be a soft 404&lt;/li&gt;
&lt;li&gt;Googlebot must be able to crawl the URL&lt;/li&gt;
&lt;li&gt;The status must remain consistent&lt;/li&gt;
&lt;li&gt;Legitimate pages must not match the rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 410 response is not an instant deindexing button. Google still needs to revisit and process the URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identify patterns before writing rules
&lt;/h2&gt;

&lt;p&gt;I did not write firewall rules based on one example URL.&lt;/p&gt;

&lt;p&gt;I collected samples from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Search Console&lt;/li&gt;
&lt;li&gt;Google &lt;code&gt;site:&lt;/code&gt; searches&lt;/li&gt;
&lt;li&gt;Search Analytics API exports&lt;/li&gt;
&lt;li&gt;Server access logs&lt;/li&gt;
&lt;li&gt;Existing malicious sitemap files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The samples revealed several repeated structures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Random query parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?a=839472938
/?q=739284729
/?x=193847293
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hacked directories
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pages/fake-product.html
/jp/random-category/
/products/23932
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Spam words inside paths
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/online-casino-offer/
/poker-jackpot/
/viagra-product.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generated HTML pages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/random-folder/product-name.html
/pages/discount-item.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules were based only on patterns confirmed on that particular website.&lt;/p&gt;

&lt;h2&gt;
  
  
  A case-specific Apache firewall
&lt;/h2&gt;

&lt;p&gt;The following is a simplified example.&lt;/p&gt;

&lt;p&gt;Do not copy it unchanged. Back up the existing &lt;code&gt;.htaccess&lt;/code&gt; file and verify that every pattern is malicious on your own site.&lt;/p&gt;

&lt;p&gt;Place the rules before the normal WordPress rewrite block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# ----------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# CASE-SPECIFIC WORDPRESS SEO SPAM FIREWALL&lt;/span&gt;
&lt;span class="c"&gt;# ----------------------------------------------------------------------&lt;/span&gt;

&lt;span class="nc"&gt;ErrorDocument&lt;/span&gt; 410 "&amp;lt;h1&amp;gt;410 Gone&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;This hacked spam &lt;span class="ss"&gt;URL&lt;/span&gt; has been permanently removed.&amp;lt;/p&amp;gt;"

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="sr"&gt; mod_rewrite.c&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;

&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# 1. CONFIRMED RANDOM QUERY PARAMETERS&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Examples:&lt;/span&gt;
&lt;span class="c"&gt;# ?a=12345678&lt;/span&gt;
&lt;span class="c"&gt;# ?q=987654321&lt;/span&gt;

&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)(?:a|q)=[0-9]{5,}(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]

&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# 2. CONFIRMED MALICIOUS DIRECTORIES&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;

&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^(?:pages|jp)(?:/|$) - [G,L,NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^products/[0-9]+(?:/|$) - [G,L,NC]

&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# 3. CONFIRMED SPAM WORDS IN THE PATH&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;

&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^.*(?:casino|gambling|viagra|cialis|poker|baccarat|roulette|jackpot).*$ - [G,L,NC]

&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# 4. OPTIONAL HTML RULE&lt;/span&gt;
&lt;span class="c"&gt;# --------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Enable only when the legitimate website has no real .html URLs.&lt;/span&gt;
&lt;span class="c"&gt;# RewriteRule ^.*\.html$ - [G,L,NC]&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apache’s &lt;code&gt;G&lt;/code&gt; flag returns HTTP 410 Gone.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;L&lt;/code&gt; flag stops further rewrite processing for the matching request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be extremely careful with WordPress &lt;code&gt;?p=&lt;/code&gt; URLs
&lt;/h2&gt;

&lt;p&gt;WordPress uses query URLs like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/?p=123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can be a legitimate post URL.&lt;/p&gt;

&lt;p&gt;During the incident, some unwanted URLs also used the same structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?p=23981
/?p=23932
/?p=23919
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocking every &lt;code&gt;?p=&lt;/code&gt; request would have been dangerous.&lt;/p&gt;

&lt;p&gt;Before creating a rule, I checked whether the suspicious ID range contained real WordPress content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;23000&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;24999&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only after confirming the relevant range did not contain legitimate content could a narrow range rule be considered.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example only: matches p values from 23000 through 24999.&lt;/span&gt;
&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (^|&amp;amp;)p=2[3-4][0-9]{3}(&amp;amp;|$) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [G,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safer rule is always better than a broad one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the rules with curl
&lt;/h2&gt;

&lt;p&gt;I tested known spam URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?a=123456789"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?q=987654321"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/pages/fake-product.html"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/jp/fake-category/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected response was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/2 410
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then tested legitimate areas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/wp-login.php"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/wp-admin/"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?s=wordpress"&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/?p=123"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those URLs needed to continue returning their normal status or redirect.&lt;/p&gt;

&lt;p&gt;Never assume a rewrite rule is safe because the syntax is valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this reduced server usage
&lt;/h2&gt;

&lt;p&gt;The firewall stopped matching requests before WordPress handled them.&lt;/p&gt;

&lt;p&gt;That meant the server did not need to fully load:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress core&lt;/li&gt;
&lt;li&gt;Plugins&lt;/li&gt;
&lt;li&gt;Theme files&lt;/li&gt;
&lt;li&gt;Database queries&lt;/li&gt;
&lt;li&gt;A styled 404 template&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benefit became noticeable because bots continued requesting old hacked URLs after the infection had been removed.&lt;/p&gt;

&lt;p&gt;For a small rule set, &lt;code&gt;.htaccess&lt;/code&gt; can be practical.&lt;/p&gt;

&lt;p&gt;For a very large or high-traffic site, equivalent rules may be better placed in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apache virtual-host configuration&lt;/li&gt;
&lt;li&gt;Nginx configuration&lt;/li&gt;
&lt;li&gt;Cloudflare&lt;/li&gt;
&lt;li&gt;A web application firewall&lt;/li&gt;
&lt;li&gt;A custom application-level route handler&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Do not block the URLs in robots.txt
&lt;/h2&gt;

&lt;p&gt;Google must request a hacked URL to observe the 404 or 410 response.&lt;/p&gt;

&lt;p&gt;If the URL is blocked in &lt;code&gt;robots.txt&lt;/code&gt;, Googlebot may be unable to see that it has been permanently removed.&lt;/p&gt;

&lt;p&gt;My approach was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove spam URLs from the legitimate sitemap&lt;/li&gt;
&lt;li&gt;Allow Googlebot to request them&lt;/li&gt;
&lt;li&gt;Return 410 for confirmed malicious patterns&lt;/li&gt;
&lt;li&gt;Monitor recrawling through logs and Search Console&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Search Console removal is only temporary
&lt;/h2&gt;

&lt;p&gt;I also used Google Search Console’s Removals tool for urgent suppression.&lt;/p&gt;

&lt;p&gt;For dedicated hacked directories, the option &lt;strong&gt;Remove all URLs with this prefix&lt;/strong&gt; was useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/pages/
https://example.com/jp/
https://example.com/products/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, Search Console removal is not the permanent fix.&lt;/p&gt;

&lt;p&gt;The permanent fix was the combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Malware removal&lt;/li&gt;
&lt;li&gt;Consistent 410 responses&lt;/li&gt;
&lt;li&gt;A clean sitemap&lt;/li&gt;
&lt;li&gt;Continued monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final lesson
&lt;/h2&gt;

&lt;p&gt;The site did not really have 242,000 separately created WordPress pages.&lt;/p&gt;

&lt;p&gt;It had a smaller number of malicious patterns generating thousands of URL variations.&lt;/p&gt;

&lt;p&gt;Once those patterns were identified, the recovery became manageable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clean the infection&lt;/li&gt;
&lt;li&gt;Collect URL samples&lt;/li&gt;
&lt;li&gt;Group them by structure&lt;/li&gt;
&lt;li&gt;Confirm that the patterns are not legitimate&lt;/li&gt;
&lt;li&gt;Return 410 before WordPress loads&lt;/li&gt;
&lt;li&gt;Temporarily suppress urgent results&lt;/li&gt;
&lt;li&gt;Monitor Googlebot and server logs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full investigation, Search Console workflow, sitemap experiment, and malware-cleanup process are documented in my &lt;a href="https://www.mdpabel.com/case-studies/remove-spam-urls-from-google/" rel="noopener noreferrer"&gt;complete spam URL removal case study&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>seo</category>
      <category>apache</category>
    </item>
    <item>
      <title>WP-VCD Malware in WordPress: How It Spreads, What It Breaks, and Why It Comes Back</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Fri, 17 Jul 2026 15:44:46 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/wp-vcd-malware-in-wordpress-how-it-spreads-what-it-breaks-and-why-it-comes-back-561o</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/wp-vcd-malware-in-wordpress-how-it-spreads-what-it-breaks-and-why-it-comes-back-561o</guid>
      <description>&lt;p&gt;During a recent WordPress malware investigation, I found this code at the beginning of a plugin file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class.plugin-modules.php'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;include_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class.plugin-modules.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is only one short line, and the rest of the file looked like normal plugin code.&lt;/p&gt;

&lt;p&gt;But the referenced file name immediately stood out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.plugin-modules.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a well-known indicator associated with &lt;strong&gt;WP-VCD&lt;/strong&gt; , a WordPress malware campaign historically distributed through nulled or pirated plugins and themes.&lt;/p&gt;

&lt;p&gt;The infected plugin in this particular case was old and inactive. However, the plugin name was not the most important finding. The same deployment method can appear inside many different premium plugins and themes downloaded from unofficial sources.&lt;/p&gt;

&lt;p&gt;That is what makes WP-VCD dangerous: the nulled plugin or theme is often only the &lt;strong&gt;initial carrier&lt;/strong&gt;. Once activated, the malware can copy itself into themes, modify WordPress core files, communicate with external servers and spread to other WordPress installations under the same hosting account.&lt;/p&gt;

&lt;p&gt;Deleting the original plugin may therefore remove the source package while leaving the active infection behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is WP-VCD malware?
&lt;/h2&gt;

&lt;p&gt;WP-VCD is a WordPress malware family that became widely known between 2017 and 2019. It was distributed mainly through websites offering pirated versions of paid WordPress plugins and themes.&lt;/p&gt;

&lt;p&gt;Instead of exploiting a vulnerability in WordPress, the campaign relied on site owners installing the malware themselves.&lt;/p&gt;

&lt;p&gt;A user would search for a free copy of a premium theme or plugin, download a modified ZIP file and install it through WordPress. The package looked functional, but additional PHP files had been added before distribution.&lt;/p&gt;

&lt;p&gt;Wordfence documented that WP-VCD packages commonly included either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.plugin-modules.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.theme-modules.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The legitimate plugin or theme was then modified so that the extra file executed when the software was activated. Wordfence also documented persistent backdoors, command-and-control communication, black-hat SEO activity and malicious advertising across infected sites. (&lt;a href="https://www.wordfence.com/blog/2019/11/wp-vcd-the-malware-you-install-on-your-own-sites/" rel="noopener noreferrer"&gt;Wordfence&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Although the campaign is old, old nulled packages are still present on websites, backups, staging installations and abandoned hosting accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to recognize the initial loader
&lt;/h2&gt;

&lt;p&gt;A typical loader looks similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class.plugin-modules.php'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;include_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class.plugin-modules.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A theme-based version may load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.theme-modules.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code itself performs three simple actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  It identifies the current directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the directory containing the current PHP file.&lt;/p&gt;

&lt;h3&gt;
  
  
  It checks whether the deployer exists
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loader does not produce an error when the referenced file is missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  It executes the deployer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;include_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file exists, PHP loads and executes it.&lt;/p&gt;

&lt;p&gt;This means the small line in &lt;code&gt;functions.php&lt;/code&gt; is not the complete malware. It is only the entry point to a second file containing the installer or deployer.&lt;/p&gt;

&lt;p&gt;The legitimate-looking structure is intentional. Wordfence noted that WP-VCD often avoided heavy obfuscation and instead hid behind ordinary filenames and code that could be overlooked during a quick inspection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The carrier is not always the active infection
&lt;/h2&gt;

&lt;p&gt;It helps to separate three parts of a WP-VCD compromise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Carrier
   ↓
Deployer
   ↓
Persistent backdoors and remote payloads

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The carrier
&lt;/h3&gt;

&lt;p&gt;This is the nulled plugin or theme downloaded by the site owner.&lt;/p&gt;

&lt;p&gt;It may still function exactly like the original premium product, making the package appear trustworthy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The deployer
&lt;/h3&gt;

&lt;p&gt;This is commonly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.plugin-modules.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.theme-modules.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its job is to install the infection in other locations.&lt;/p&gt;

&lt;h3&gt;
  
  
  The persistent infection
&lt;/h3&gt;

&lt;p&gt;After deployment, malicious code may continue running from theme files, WordPress core files or neighbouring WordPress installations.&lt;/p&gt;

&lt;p&gt;This distinction explains why an inactive plugin may still be important evidence.&lt;/p&gt;

&lt;p&gt;The plugin may no longer be executing, but it may identify how the compromise originally started.&lt;/p&gt;

&lt;h2&gt;
  
  
  How WP-VCD spreads after activation
&lt;/h2&gt;

&lt;p&gt;Historical versions followed a multi-stage deployment process.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The nulled software is activated
&lt;/h3&gt;

&lt;p&gt;The modified plugin or theme loads the added deployer file.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Installed themes are scanned
&lt;/h3&gt;

&lt;p&gt;The deployer searches the site for WordPress themes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Backdoor code is added to theme files
&lt;/h3&gt;

&lt;p&gt;WP-VCD has been documented injecting code into the &lt;code&gt;functions.php&lt;/code&gt; file of every installed theme, not only the active theme.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Modification times may be restored
&lt;/h3&gt;

&lt;p&gt;After editing the files, some versions restored their previous modification timestamps. This made the files look older and reduced the usefulness of sorting by “Last Modified.”&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The site is registered with command-and-control infrastructure
&lt;/h3&gt;

&lt;p&gt;The malware can send information about the infected site to an attacker-controlled server.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Other WordPress installations are discovered
&lt;/h3&gt;

&lt;p&gt;The deployer may move through the hosting directory structure and look for additional WordPress sites.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. WordPress core is modified
&lt;/h3&gt;

&lt;p&gt;Documented versions created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-includes/wp-vcd.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and modified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-includes/post.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so that &lt;code&gt;wp-vcd.php&lt;/code&gt; executed during normal WordPress requests.&lt;/p&gt;

&lt;p&gt;This created a persistent reinfection loop. If a backdoor was removed from a theme, &lt;code&gt;wp-vcd.php&lt;/code&gt; could write it back during a later page request.&lt;/p&gt;

&lt;p&gt;A simplified infection chain looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nulled plugin or theme
        ↓
class.plugin-modules.php
or class.theme-modules.php
        ↓
Backdoors injected into theme functions.php files
        ↓
wp-includes/wp-vcd.php created
        ↓
wp-includes/post.php modified
        ↓
Remote instructions and additional payloads
        ↓
Other WordPress installations may be infected

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A useful forensic clue: the deployer may remove its own installer
&lt;/h2&gt;

&lt;p&gt;One of the more interesting WP-VCD behaviours is that the initial deployer may remove its installation code after it finishes spreading the malware.&lt;/p&gt;

&lt;p&gt;The original &lt;code&gt;class.plugin-modules.php&lt;/code&gt; or &lt;code&gt;class.theme-modules.php&lt;/code&gt; file may therefore appear nearly empty when it is discovered later.&lt;/p&gt;

&lt;p&gt;Wordfence documented versions that removed the code located between their installation markers and left only a line similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nb"&gt;error_reporting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can create confusion during cleanup.&lt;/p&gt;

&lt;p&gt;Someone may open the file, see almost nothing dangerous and assume it was a false positive. In reality, the file may already have completed its purpose and copied the backdoors elsewhere.&lt;/p&gt;

&lt;p&gt;This also means the original carrier can be useful for identifying the first infected site in a hosting account. Other sites may contain &lt;code&gt;wp-vcd.php&lt;/code&gt; and modified theme files, while only the original installation contains the initial plugin or theme deployer.&lt;/p&gt;

&lt;h1&gt;
  
  
  What problems can WP-VCD create?
&lt;/h1&gt;

&lt;p&gt;The visible symptoms are not identical on every infected site.&lt;/p&gt;

&lt;p&gt;WP-VCD’s controllers could send additional code from remote infrastructure, allowing the behaviour to change without updating the original nulled plugin or theme. Historically documented monetization included black-hat SEO and malicious advertising. (&lt;a href="https://www.wordfence.com/blog/2019/11/wp-vcd-the-malware-you-install-on-your-own-sites/" rel="noopener noreferrer"&gt;Wordfence&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwpyd05cf8ye5m0qemv9f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwpyd05cf8ye5m0qemv9f.webp" alt="Nulled plugin" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Popups and unwanted advertising
&lt;/h2&gt;

&lt;p&gt;Visitors may experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unexpected popups&lt;/li&gt;
&lt;li&gt;New browser tabs&lt;/li&gt;
&lt;li&gt;Full-page advertisements&lt;/li&gt;
&lt;li&gt;Fake download offers&lt;/li&gt;
&lt;li&gt;Browser-notification prompts&lt;/li&gt;
&lt;li&gt;Adult, casino or scam advertisements&lt;/li&gt;
&lt;li&gt;Advertisements that appear only occasionally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The administrator may not see the same behaviour during every test.&lt;/p&gt;

&lt;p&gt;Conditional malware commonly limits how often it targets one visitor, or avoids displaying the payload to logged-in administrators. As a result, a site owner may check the homepage and conclude that nothing is wrong while actual visitors continue receiving unwanted content.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Redirects to suspicious websites
&lt;/h2&gt;

&lt;p&gt;Remote payloads may redirect visitors away from the legitimate site.&lt;/p&gt;

&lt;p&gt;The redirect may happen only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On mobile devices&lt;/li&gt;
&lt;li&gt;On the first visit&lt;/li&gt;
&lt;li&gt;After arriving from a search engine&lt;/li&gt;
&lt;li&gt;On selected posts&lt;/li&gt;
&lt;li&gt;In specific countries&lt;/li&gt;
&lt;li&gt;While logged out&lt;/li&gt;
&lt;li&gt;Without an existing browser cookie&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These conditions make the infection difficult to reproduce consistently.&lt;/p&gt;

&lt;p&gt;The website can appear normal on the owner’s computer but redirect real customers to advertising, fake downloads, scams or other unwanted destinations.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Hidden backlinks and black-hat SEO
&lt;/h2&gt;

&lt;p&gt;WP-VCD used infected sites to promote the websites distributing the nulled software.&lt;/p&gt;

&lt;p&gt;The campaign could inject backlinks or other SEO-related code into compromised websites. This created a self-supporting distribution system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nulled-software sites distributed infected packages.&lt;/li&gt;
&lt;li&gt;New victims installed those packages.&lt;/li&gt;
&lt;li&gt;Compromised sites generated backlinks or traffic for the distributors.&lt;/li&gt;
&lt;li&gt;The distributor websites gained greater search visibility.&lt;/li&gt;
&lt;li&gt;More people discovered and downloaded the infected packages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wordfence found that the campaign could remotely deploy SEO-manipulation code to infected sites rather than storing all monetization behaviour inside the original deployer.&lt;/p&gt;

&lt;p&gt;For the victim, this may result in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hidden links&lt;/li&gt;
&lt;li&gt;Spam anchor text&lt;/li&gt;
&lt;li&gt;Links appended to content&lt;/li&gt;
&lt;li&gt;Off-screen HTML&lt;/li&gt;
&lt;li&gt;Unrelated keywords in Google Search Console&lt;/li&gt;
&lt;li&gt;Search impressions for suspicious terms&lt;/li&gt;
&lt;li&gt;Reduced trust in legitimate pages&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Remote PHP execution
&lt;/h2&gt;

&lt;p&gt;The theme backdoor could receive new code from command-and-control servers and execute it on compromised websites.&lt;/p&gt;

&lt;p&gt;This is more serious than one fixed redirect.&lt;/p&gt;

&lt;p&gt;Once an attacker can deliver and execute new PHP, the visible behaviour can be changed at any time. One infected website may show popups, another may inject links, and another may appear quiet while retaining the same underlying access.&lt;/p&gt;

&lt;p&gt;Wordfence documented fallback C2 addresses and a mechanism for changing the primary C2 domain, helping the campaign survive individual infrastructure disruptions. (&lt;a href="https://www.wordfence.com/blog/2019/12/wp-vcd-evolves-to-remain-most-prevalent-wordpress-infection/" rel="noopener noreferrer"&gt;Wordfence&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Cached payloads in &lt;code&gt;wp-tmp.php&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;WP-VCD also used a fallback mechanism for remote payloads.&lt;/p&gt;

&lt;p&gt;Code received from the C2 server could be saved locally as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-tmp.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the remote servers later failed to respond, the malware could execute the most recently cached payload instead.&lt;/p&gt;

&lt;p&gt;This means an unavailable C2 domain does not automatically make the site clean. Previously delivered code may still exist locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Rogue WordPress administrator accounts
&lt;/h2&gt;

&lt;p&gt;Earlier WP-VCD variants were documented creating an administrator account with these details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Username: 100010010
Email: te@ea.st

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That account would appear in the normal WordPress database tables and could provide another way to access the dashboard.&lt;/p&gt;

&lt;p&gt;A rogue administrator can be used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload another plugin&lt;/li&gt;
&lt;li&gt;Edit theme files&lt;/li&gt;
&lt;li&gt;Disable security tools&lt;/li&gt;
&lt;li&gt;Add spam content&lt;/li&gt;
&lt;li&gt;Create more accounts&lt;/li&gt;
&lt;li&gt;Change settings&lt;/li&gt;
&lt;li&gt;Re-establish access after file cleanup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The absence of this exact username does not prove the site is safe. It was associated with particular versions, and different or secondary payloads may create different accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Repeated reinfection
&lt;/h2&gt;

&lt;p&gt;One of the most frustrating symptoms is malware that appears to return after removal.&lt;/p&gt;

&lt;p&gt;For example, an administrator deletes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-vcd.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or removes malicious code from one theme.&lt;/p&gt;

&lt;p&gt;A few hours later, the file or code reappears.&lt;/p&gt;

&lt;p&gt;This normally means another persistence point remains active:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A different infected theme&lt;/li&gt;
&lt;li&gt;A modified &lt;code&gt;wp-includes/post.php&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The original deployer&lt;/li&gt;
&lt;li&gt;A cached payload&lt;/li&gt;
&lt;li&gt;Another compromised plugin&lt;/li&gt;
&lt;li&gt;An infected staging site&lt;/li&gt;
&lt;li&gt;Another WordPress installation in the same account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The malware is not returning from nowhere. A component that was not removed is rebuilding the deleted component.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Multiple infected themes
&lt;/h2&gt;

&lt;p&gt;WP-VCD did not limit itself to the active theme. Historical deployers scanned installed themes and injected their &lt;code&gt;functions.php&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;A website might therefore contain malicious code in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-content/themes/current-theme/functions.php
/wp-content/themes/old-theme/functions.php
/wp-content/themes/default-theme/functions.php
/wp-content/themes/unused-theme/functions.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cleaning only the active theme can leave several copies behind.&lt;/p&gt;

&lt;p&gt;Unused themes should either be inspected and replaced or removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Cross-site infection inside one hosting account
&lt;/h2&gt;

&lt;p&gt;WP-VCD’s lateral propagation is one of its most important behaviours.&lt;/p&gt;

&lt;p&gt;Documented versions moved upward through the directory structure and then searched for additional WordPress installations in the same hosting environment. Detected sites could receive &lt;code&gt;wp-vcd.php&lt;/code&gt; and the related core modification.&lt;/p&gt;

&lt;p&gt;This matters when a hosting account contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/public_html/
/public_html/blog/
/public_html/shop/
/public_html/staging/
/public_html/old-site/
/public_html/client-site/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The forgotten staging site may be the infection source.&lt;/p&gt;

&lt;p&gt;Cleaning the main domain while leaving another installation compromised can result in reinfection.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. WordPress core modification
&lt;/h2&gt;

&lt;p&gt;A modified core file can execute during normal page requests while the site continues to work.&lt;/p&gt;

&lt;p&gt;This makes the infection less visible than a broken site.&lt;/p&gt;

&lt;p&gt;The most notable historical indicators include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-includes/wp-vcd.php
/wp-includes/wp-tmp.php
/wp-includes/post.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, cleanup should not be limited to those exact files. Once unauthorized PHP has executed, other files may also have been added or altered.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Misleading file dates
&lt;/h2&gt;

&lt;p&gt;The deployer could reset the modification timestamps of infected theme files after writing the backdoor.&lt;/p&gt;

&lt;p&gt;Therefore, this approach is not reliable by itself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I sorted the files by modification date and found nothing recent.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A file showing a date from several years ago may still have been modified later.&lt;/p&gt;

&lt;p&gt;Use file dates together with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean-package comparisons&lt;/li&gt;
&lt;li&gt;Checksums&lt;/li&gt;
&lt;li&gt;Code searches&lt;/li&gt;
&lt;li&gt;File hashes&lt;/li&gt;
&lt;li&gt;Access logs&lt;/li&gt;
&lt;li&gt;Malware signatures&lt;/li&gt;
&lt;li&gt;Unexpected ownership or permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  12. Search-engine and reputation damage
&lt;/h2&gt;

&lt;p&gt;Redirects, unwanted advertising and injected links can damage a domain’s search visibility and reputation.&lt;/p&gt;

&lt;p&gt;Possible consequences include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Irrelevant queries in Search Console&lt;/li&gt;
&lt;li&gt;Ranking loss&lt;/li&gt;
&lt;li&gt;Spam pages or links being indexed&lt;/li&gt;
&lt;li&gt;Browser warnings&lt;/li&gt;
&lt;li&gt;Antivirus detections&lt;/li&gt;
&lt;li&gt;Hosting malware notices&lt;/li&gt;
&lt;li&gt;Reduced visitor trust&lt;/li&gt;
&lt;li&gt;Lower conversion rates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These effects may remain after the visible malware has been removed. Search engines and security vendors may need time to recrawl the website, and some warnings require a review request after the cleanup is completed.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Hosting suspension and resource usage
&lt;/h2&gt;

&lt;p&gt;A hosting company may suspend an infected account after finding malicious PHP, spam redirects, repeated reinfection or unsafe activity.&lt;/p&gt;

&lt;p&gt;Remote requests, repeated file writes and injected scripts may also contribute to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher CPU usage&lt;/li&gt;
&lt;li&gt;Increased PHP activity&lt;/li&gt;
&lt;li&gt;Slow page generation&lt;/li&gt;
&lt;li&gt;Additional bandwidth use&lt;/li&gt;
&lt;li&gt;Intermittent timeouts&lt;/li&gt;
&lt;li&gt;Large malware scan reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A host may provide a list containing only the files its scanner recognized. That list should not be treated as a complete map of the compromise.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Possible exposure of stored secrets
&lt;/h2&gt;

&lt;p&gt;Finding WP-VCD confirms that unauthorized PHP executed on the server.&lt;/p&gt;

&lt;p&gt;It does not automatically prove that every password or API key was stolen. However, arbitrary PHP can read files and access information available to the hosting account.&lt;/p&gt;

&lt;p&gt;Credentials should therefore be considered untrusted until rotated, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress administrator passwords&lt;/li&gt;
&lt;li&gt;Hosting credentials&lt;/li&gt;
&lt;li&gt;FTP or SFTP accounts&lt;/li&gt;
&lt;li&gt;Database credentials&lt;/li&gt;
&lt;li&gt;SMTP passwords&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;WordPress authentication salts&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Does WP-VCD store malware in the database?
&lt;/h1&gt;

&lt;p&gt;WP-VCD is primarily a &lt;strong&gt;file-based infection&lt;/strong&gt; , but database artifacts may also exist.&lt;/p&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;The main components generally appear as PHP files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class.plugin-modules.php
class.theme-modules.php
wp-vcd.php
wp-tmp.php
modified theme functions.php files
modified WordPress core files

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the database may contain secondary evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rogue users
&lt;/h2&gt;

&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp_users
wp_usermeta

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for unknown administrators, especially the historically documented &lt;code&gt;100010010&lt;/code&gt; account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduled events
&lt;/h2&gt;

&lt;p&gt;WordPress cron events are stored inside the &lt;code&gt;cron&lt;/code&gt; option in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp_options

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not every WP-VCD infection creates a malicious cron event, but the schedule should still be reviewed for unknown hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suspicious options and widgets
&lt;/h2&gt;

&lt;p&gt;Additional payloads may store scripts, links or settings in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp_options
widget_* options
theme_mods_* options

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Injected posts or metadata
&lt;/h2&gt;

&lt;p&gt;Search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp_posts
wp_postmeta

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for unexpected scripts, hidden links, spam posts and unrelated content.&lt;/p&gt;

&lt;p&gt;A clean database does not prove that WP-VCD has been removed. The primary persistence may remain entirely inside files.&lt;/p&gt;

&lt;p&gt;Likewise, deleting one suspicious database user does not clean modified PHP files.&lt;/p&gt;

&lt;h1&gt;
  
  
  Can an inactive plugin still be dangerous?
&lt;/h1&gt;

&lt;p&gt;An inactive plugin normally does not load through WordPress’s standard active-plugin system.&lt;/p&gt;

&lt;p&gt;But an inactive nulled plugin can still be important in three ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  It may have been active in the past
&lt;/h2&gt;

&lt;p&gt;The deployer may already have copied the malware into themes and WordPress core files.&lt;/p&gt;

&lt;p&gt;Deactivating the original plugin does not reverse those changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another file may load it directly
&lt;/h2&gt;

&lt;p&gt;A compromised theme, MU plugin or custom PHP file can load code without depending on WordPress’s active-plugin list.&lt;/p&gt;

&lt;h2&gt;
  
  
  It can identify the original entry point
&lt;/h2&gt;

&lt;p&gt;An inactive nulled package containing &lt;code&gt;class.plugin-modules.php&lt;/code&gt; may reveal how the server was first compromised.&lt;/p&gt;

&lt;p&gt;Therefore, the correct conclusion is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The plugin is inactive, so it cannot be responsible.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The more accurate conclusion is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The plugin may no longer be the active persistence point, but it may have been the original carrier.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  How to detect WP-VCD
&lt;/h1&gt;

&lt;p&gt;Take a full backup before deleting or editing anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search for known filenames
&lt;/h2&gt;

&lt;p&gt;With SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /path/to/public_html &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"class.plugin-modules.php"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"class.theme-modules.php"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"wp-vcd.php"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"wp-tmp.php"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Search for known code markers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-RInE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s1"&gt;'class\.(plugin|theme)-modules\.php|WP_V_CD|WP_CD_CODE|theme_temp_setup|wp_temp_setup|100010010'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
/path/to/public_html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Search for the loader
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-RIn&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"dirname( __FILE__ ).*class.plugin-modules.php"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
/path/to/public_html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also search for the theme equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-RIn&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"dirname( __FILE__ ).*class.theme-modules.php"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
/path/to/public_html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify WordPress core
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp core verify-checksums

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WP-CLI compares installed WordPress core files against official WordPress.org checksums and reports files that do not match. It performs this check before fully loading WordPress. (&lt;a href="https://developer.wordpress.org/cli/commands/core/verify-checksums/" rel="noopener noreferrer"&gt;WordPress Developer Resources&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;To include unexpected files in the WordPress root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp core verify-checksums &lt;span class="nt"&gt;--include-root&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify WordPress.org plugins
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp plugin verify-checksums &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WP-CLI can compare plugins available through WordPress.org against their official checksums. (&lt;a href="https://developer.wordpress.org/cli/commands/plugin/verify-checksums/" rel="noopener noreferrer"&gt;WordPress Developer Resources&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Premium, custom and privately distributed plugins may not have public checksums. Compare those manually against clean packages downloaded from their original vendors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review administrator users
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp user list &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;administrator

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check every account with the site owner.&lt;/p&gt;

&lt;p&gt;Do not assume an account is legitimate because its username looks normal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review scheduled events
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp cron event list

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Investigate unfamiliar hooks, especially those calling unknown files or functions.&lt;/p&gt;

&lt;p&gt;Do not delete every cron event. WordPress and legitimate plugins depend on scheduled tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the database
&lt;/h2&gt;

&lt;p&gt;Replace &lt;code&gt;wp_&lt;/code&gt; with the site’s real table prefix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_registered&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_users&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;user_registered&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check important options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;option_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;option_preview&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_options&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;option_name&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'active_plugins'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'recently_activated'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'cron'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Search for readable indicators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;option_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;option_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;option_preview&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_options&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;option_value&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%wp-vcd%'&lt;/span&gt;
   &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;option_value&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%wp-tmp%'&lt;/span&gt;
   &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;option_value&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%class.plugin-modules%'&lt;/span&gt;
   &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;option_value&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%class.theme-modules%'&lt;/span&gt;
   &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;option_value&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%100010010%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No results do not guarantee a clean database. Payloads may be encoded, stored under unrelated names or absent from the database completely.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to remove WP-VCD completely
&lt;/h1&gt;

&lt;p&gt;Removing only the first loader line is not enough.&lt;/p&gt;

&lt;p&gt;A full cleanup should cover the entire hosting account.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Preserve evidence
&lt;/h2&gt;

&lt;p&gt;Before deleting the malware, save a quarantined copy outside the public web directory.&lt;/p&gt;

&lt;p&gt;Record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suspicious filenames&lt;/li&gt;
&lt;li&gt;File hashes&lt;/li&gt;
&lt;li&gt;File sizes&lt;/li&gt;
&lt;li&gt;Modification times&lt;/li&gt;
&lt;li&gt;Plugin and theme names&lt;/li&gt;
&lt;li&gt;Database findings&lt;/li&gt;
&lt;li&gt;Unknown users&lt;/li&gt;
&lt;li&gt;Scan reports&lt;/li&gt;
&lt;li&gt;Relevant access-log entries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can help identify the original carrier and explain later reinfection.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Remove all nulled software
&lt;/h2&gt;

&lt;p&gt;Delete the entire infected plugin or theme directory.&lt;/p&gt;

&lt;p&gt;Do not reinstall the same ZIP.&lt;/p&gt;

&lt;p&gt;Replace required software with an official copy from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The original developer&lt;/li&gt;
&lt;li&gt;A reputable marketplace&lt;/li&gt;
&lt;li&gt;The WordPress.org repository&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Inspect every theme
&lt;/h2&gt;

&lt;p&gt;Check the &lt;code&gt;functions.php&lt;/code&gt; file of every installed theme.&lt;/p&gt;

&lt;p&gt;Do not limit the investigation to the active theme.&lt;/p&gt;

&lt;p&gt;Replace official themes with clean copies and remove unused themes that are not required.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Replace modified WordPress core files
&lt;/h2&gt;

&lt;p&gt;Verify checksums and replace modified core files with clean files from the matching official WordPress release.&lt;/p&gt;

&lt;p&gt;Pay particular attention to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-includes/post.php
/wp-includes/wp-vcd.php
/wp-includes/wp-tmp.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not overwrite &lt;code&gt;wp-content&lt;/code&gt; or &lt;code&gt;wp-config.php&lt;/code&gt; without understanding what will be lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Compare all plugins with clean packages
&lt;/h2&gt;

&lt;p&gt;For WordPress.org plugins, use checksum verification.&lt;/p&gt;

&lt;p&gt;For premium plugins, compare the complete directory against a newly downloaded licensed copy.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional PHP files&lt;/li&gt;
&lt;li&gt;Modified plugin headers&lt;/li&gt;
&lt;li&gt;Code before the normal opening comments&lt;/li&gt;
&lt;li&gt;Unexpected remote URLs&lt;/li&gt;
&lt;li&gt;File-writing functions&lt;/li&gt;
&lt;li&gt;Unknown include statements&lt;/li&gt;
&lt;li&gt;Third-party distributor branding&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Inspect MU plugins and drop-ins
&lt;/h2&gt;

&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/wp-content/mu-plugins/
/wp-content/advanced-cache.php
/wp-content/db.php
/wp-content/object-cache.php
/wp-content/sunrise.php

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These files can execute independently from the normal active-plugin list.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Review the database
&lt;/h2&gt;

&lt;p&gt;Remove unauthorized administrator accounts.&lt;/p&gt;

&lt;p&gt;Inspect cron, options, widgets, posts and post metadata.&lt;/p&gt;

&lt;p&gt;Do not perform broad search-and-delete operations without reviewing the results. Legitimate page builders and plugins may contain scripts, encoded values and serialized data.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Scan every site under the account
&lt;/h2&gt;

&lt;p&gt;Include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Main websites&lt;/li&gt;
&lt;li&gt;Addon domains&lt;/li&gt;
&lt;li&gt;Subdomains&lt;/li&gt;
&lt;li&gt;Staging sites&lt;/li&gt;
&lt;li&gt;Development copies&lt;/li&gt;
&lt;li&gt;Old installations&lt;/li&gt;
&lt;li&gt;Forgotten backups containing executable PHP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One compromised installation can undermine the cleanup of the others.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Rotate credentials
&lt;/h2&gt;

&lt;p&gt;Change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting password&lt;/li&gt;
&lt;li&gt;WordPress administrator passwords&lt;/li&gt;
&lt;li&gt;SFTP and FTP passwords&lt;/li&gt;
&lt;li&gt;SSH credentials&lt;/li&gt;
&lt;li&gt;Database password&lt;/li&gt;
&lt;li&gt;Control-panel credentials&lt;/li&gt;
&lt;li&gt;Connected email passwords where necessary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regenerate WordPress authentication salts after the files are clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Update and harden WordPress
&lt;/h2&gt;

&lt;p&gt;After cleaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update WordPress&lt;/li&gt;
&lt;li&gt;Update plugins and themes&lt;/li&gt;
&lt;li&gt;Remove unused software&lt;/li&gt;
&lt;li&gt;Disable dashboard file editing&lt;/li&gt;
&lt;li&gt;Enable two-factor authentication&lt;/li&gt;
&lt;li&gt;Review file permissions&lt;/li&gt;
&lt;li&gt;Restrict administrator access&lt;/li&gt;
&lt;li&gt;Add file-integrity monitoring&lt;/li&gt;
&lt;li&gt;Review access logs&lt;/li&gt;
&lt;li&gt;Keep off-site backups&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11. Retest as an ordinary visitor
&lt;/h2&gt;

&lt;p&gt;Test the website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;While logged out&lt;/li&gt;
&lt;li&gt;In a private browser&lt;/li&gt;
&lt;li&gt;From a mobile device&lt;/li&gt;
&lt;li&gt;On another network&lt;/li&gt;
&lt;li&gt;Through several landing pages&lt;/li&gt;
&lt;li&gt;After arriving through a search result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not rely only on the administrator’s usual browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Request security and search reviews only after cleanup
&lt;/h2&gt;

&lt;p&gt;After confirming the website is clean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request a new hosting scan&lt;/li&gt;
&lt;li&gt;Review Google Search Console&lt;/li&gt;
&lt;li&gt;Remove spam URLs where appropriate&lt;/li&gt;
&lt;li&gt;Request blacklist reconsideration&lt;/li&gt;
&lt;li&gt;Monitor indexed pages and search queries&lt;/li&gt;
&lt;li&gt;Watch for file changes and reinfection&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Common WP-VCD cleanup mistakes
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Deleting only the nulled plugin
&lt;/h2&gt;

&lt;p&gt;The plugin may have already copied backdoors elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing only &lt;code&gt;wp-vcd.php&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;An infected theme or neighbouring installation may recreate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning only the active theme
&lt;/h2&gt;

&lt;p&gt;Inactive themes may contain the same backdoor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trusting old timestamps
&lt;/h2&gt;

&lt;p&gt;WP-VCD could restore earlier modification times after changing files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignoring the database
&lt;/h2&gt;

&lt;p&gt;Rogue users or secondary artifacts may remain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking only the database
&lt;/h2&gt;

&lt;p&gt;The main persistence is usually file-based.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scanning only the visible domain
&lt;/h2&gt;

&lt;p&gt;Other WordPress installations under the account may be infected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusing an old backup without scanning it
&lt;/h2&gt;

&lt;p&gt;A backup created after the original infection may contain the same nulled package and backdoors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requesting blacklist removal too early
&lt;/h2&gt;

&lt;p&gt;A failed review can delay recovery if persistence mechanisms are still present.&lt;/p&gt;

&lt;h1&gt;
  
  
  Indicators of compromise
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Indicator&lt;/th&gt;
&lt;th&gt;Location or type&lt;/th&gt;
&lt;th&gt;Confidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;class.plugin-modules.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nulled plugin&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;class.theme-modules.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nulled theme&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loader referencing either modules file&lt;/td&gt;
&lt;td&gt;Modified PHP file&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wp-includes/wp-vcd.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WordPress core area&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modified &lt;code&gt;wp-includes/post.php&lt;/code&gt; loading &lt;code&gt;wp-vcd.php&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;WordPress core&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;WP_V_CD&lt;/code&gt; or &lt;code&gt;WP_CD_CODE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Code marker&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Administrator &lt;code&gt;100010010&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;High when present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wp-tmp.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cached payload&lt;/td&gt;
&lt;td&gt;Medium to high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;theme_temp_setup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Function name&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unofficial distributor branding&lt;/td&gt;
&lt;td&gt;Supply-chain evidence&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old timestamps on modified theme files&lt;/td&gt;
&lt;td&gt;Forensic clue&lt;/td&gt;
&lt;td&gt;Low by itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Popups or conditional redirects&lt;/td&gt;
&lt;td&gt;Symptom&lt;/td&gt;
&lt;td&gt;Low by itself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The indicators should be considered together. A generic function such as &lt;code&gt;include_once()&lt;/code&gt; or &lt;code&gt;file_get_contents()&lt;/code&gt; is not malicious by itself.&lt;/p&gt;

&lt;h1&gt;
  
  
  A recent real-world discovery
&lt;/h1&gt;

&lt;p&gt;In the case that led me to revisit WP-VCD, I found the loader inside an inactive nulled premium plugin.&lt;/p&gt;

&lt;p&gt;The plugin name was not the important part. Other users may find the same loader inside completely different plugins or themes.&lt;/p&gt;

&lt;p&gt;The important details were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The package had been redistributed by an unofficial source.&lt;/li&gt;
&lt;li&gt;Its legitimate PHP file had been modified.&lt;/li&gt;
&lt;li&gt;It loaded &lt;code&gt;class.plugin-modules.php&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The plugin was inactive but had probably been installed for years.&lt;/li&gt;
&lt;li&gt;The rest of the hosting account still needed to be examined.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This case is a reminder that old malware can survive as forgotten code even when its original campaign is no longer receiving much attention.&lt;/p&gt;

&lt;p&gt;A website does not become clean because the malicious plugin is inactive, the old C2 domain is unavailable or the site currently shows no redirect.&lt;/p&gt;

&lt;p&gt;The real question is whether the deployer had already completed its work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Frequently asked questions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;class.plugin-modules.php&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;It is a filename historically used as the primary deployer inside plugins modified by the WP-VCD campaign.&lt;/p&gt;

&lt;p&gt;It is not a standard WordPress core file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;class.theme-modules.php&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;It serves a similar purpose inside a modified or nulled theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the one-line loader the complete malware?
&lt;/h2&gt;

&lt;p&gt;No. It checks for another file and executes it. The referenced file may then install backdoors in other locations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can WP-VCD infect an official plugin?
&lt;/h2&gt;

&lt;p&gt;The campaign generally distributed modified copies of legitimate paid software through unofficial sites. The original plugin developer may have had no involvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does an inactive plugin still run?
&lt;/h2&gt;

&lt;p&gt;Normally it does not load through the standard WordPress active-plugin system. However, it may have executed earlier and created persistent files elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does WP-VCD infect the WordPress database?
&lt;/h2&gt;

&lt;p&gt;Its main persistence is generally file-based. Some variants created a rogue administrator, and additional payloads may leave options, cron events or injected content in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does WP-VCD come back after removal?
&lt;/h2&gt;

&lt;p&gt;Another infected file, theme, site or cached payload is probably recreating the component that was deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can WP-VCD infect more than one website?
&lt;/h2&gt;

&lt;p&gt;Documented versions searched for other WordPress installations in the same hosting environment and deployed additional malware to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is deleting &lt;code&gt;wp-vcd.php&lt;/code&gt; enough?
&lt;/h2&gt;

&lt;p&gt;No. You must identify what created it and inspect themes, core files, plugins, database records and other installations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can a scanner remove WP-VCD completely?
&lt;/h2&gt;

&lt;p&gt;A scanner can identify known signatures, but complete remediation also requires file-integrity verification, clean-package comparison, database review and an account-wide investigation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final conclusion
&lt;/h1&gt;

&lt;p&gt;WP-VCD is an old WordPress malware family, but the lessons from it remain relevant.&lt;/p&gt;

&lt;p&gt;The campaign succeeded because users installed compromised premium plugins and themes themselves. Once activated, the deployer could modify themes, add persistent core files, contact external servers and spread to additional WordPress installations.&lt;/p&gt;

&lt;p&gt;The original nulled package may later be inactive, deleted or almost empty.&lt;/p&gt;

&lt;p&gt;That does not mean the compromise is gone.&lt;/p&gt;

&lt;p&gt;The most important principle is to distinguish between the &lt;strong&gt;carrier&lt;/strong&gt; and the &lt;strong&gt;active persistence&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The nulled package brought the malware in.
The deployer spread it.
Other files may now be keeping it alive.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;class.plugin-modules.php&lt;/code&gt;, &lt;code&gt;class.theme-modules.php&lt;/code&gt; or &lt;code&gt;wp-vcd.php&lt;/code&gt; is found, treat the entire hosting environment as potentially compromised.&lt;/p&gt;

&lt;p&gt;Preserve the evidence, replace unauthorized software, verify core files, inspect every theme, review the database, scan neighbouring installations and rotate credentials.&lt;/p&gt;

&lt;p&gt;Anything less can leave the component that brings the malware back.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SiteGround Suspended a Client Site for Malware — Removing a Tiny File Manager Backdoor (single-compiler.php)</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Fri, 19 Jun 2026 00:23:37 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/siteground-suspended-a-client-site-for-malware-removing-a-tiny-file-manager-backdoor-139</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/siteground-suspended-a-client-site-for-malware-removing-a-tiny-file-manager-backdoor-139</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick answer:&lt;/strong&gt; SiteGround suspends a site and shows &lt;em&gt;“Malware detected for your site(s)”&lt;/em&gt; when its scanner finds malicious PHP on your hosting. In this cleanup the culprit was &lt;strong&gt;single-compiler.php&lt;/strong&gt; — a renamed copy of the open-source &lt;strong&gt;Tiny File Manager&lt;/strong&gt; hidden inside the WooDmart theme, plus 13 camouflaged secondary backdoors. Removing every flagged file, replacing the theme and core from clean sources, then using SiteGround’s &lt;em&gt;Request Scan&lt;/em&gt; lifted the suspension the same day.&lt;/p&gt;

&lt;p&gt;The two messages that brought this client to me&lt;/p&gt;

&lt;p&gt;Public site: &lt;em&gt;“This site is currently unavailable. If you’re the owner of this website, please contact your hosting provider to get this resolved.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;SiteGround dashboard: &lt;em&gt;“Malware detected for your site(s). Web access for the affected site(s) under your hosting plan has been temporarily suspended until the malware is removed.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A client’s WooCommerce store went dark mid-week. Loading the domain returned a plain SiteGround holding page instead of the storefront, and the SiteGround Client Area showed a red malware notice. This is a walkthrough of exactly what SiteGround flagged, what the malware actually was, how I removed it, and how I got the suspension lifted — written from the real cleanup, not a generic checklist. The specific files differ from site to site; the SiteGround flow and the method below stay the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Symptom 1: “This site is currently unavailable”
&lt;/h2&gt;

&lt;p&gt;The first thing the client saw was the front end replaced by SiteGround’s suspension page. Unlike a normal WordPress crash, there was no “critical error,” no white screen — just a hosting-level notice with a rain-cloud icon. That distinction matters: when the message reads &lt;em&gt;“If you’re the owner of this website, please contact your hosting provider to get this resolved,”&lt;/em&gt; the problem isn’t in WordPress, it’s that SiteGround has quarantined the account at the server level. Nothing you change in &lt;code&gt;wp-admin&lt;/code&gt; will bring it back, because &lt;code&gt;wp-admin&lt;/code&gt; is offline too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi8xtj1a1nt9zlad0gznc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi8xtj1a1nt9zlad0gznc.png" alt="SiteGround suspended page reading This site is currently unavailable contact your hosting provider"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The public-facing SiteGround suspension page — the site is quarantined at the server level, not just broken in WordPress.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Symptom 2: “Malware detected for your site(s)” in the SiteGround dashboard
&lt;/h2&gt;

&lt;p&gt;Logging into the SiteGround Client Area, the &lt;strong&gt;Important Notifications&lt;/strong&gt; panel carried the suspension reason: &lt;em&gt;“Malware detected for your site(s). Web access for the affected site(s) under your hosting plan has been temporarily suspended until the malware is removed.”&lt;/em&gt; Next to it sat a &lt;strong&gt;Review&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0r26glk6ybdeduzjs6dn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0r26glk6ybdeduzjs6dn.png" alt="SiteGround dashboard notification Malware detected for your sites web access temporarily suspended"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;SiteGround’s malware notification in the Client Area, with the Review button that opens the evidence.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Clicking &lt;strong&gt;Review&lt;/strong&gt; opens a long advisory (sections like &lt;em&gt;“Take actions to avoid future infection”&lt;/em&gt; and &lt;em&gt;“Using Site Scanner service is highly recommended”&lt;/em&gt;), and at the bottom four buttons: &lt;strong&gt;Close&lt;/strong&gt; , &lt;strong&gt;View Evidence&lt;/strong&gt; , &lt;strong&gt;Request Cleanup&lt;/strong&gt; , and &lt;strong&gt;More&lt;/strong&gt;. &lt;strong&gt;View Evidence&lt;/strong&gt; is the one that matters — it lists the exact files SiteGround’s scanner flagged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy0r1cmjwrt9hndu26ji5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy0r1cmjwrt9hndu26ji5.png" alt="SiteGround malware review modal with Close View Evidence Request Cleanup and More buttons"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Review modal. “View Evidence” reveals the flagged file paths; “Request Cleanup” is SiteGround’s paid option.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The evidence: 14 suspicious files
&lt;/h2&gt;

&lt;p&gt;SiteGround reported &lt;strong&gt;14 suspicious files&lt;/strong&gt; , each with a full server path and a detection timestamp. The list is the real value here — it tells you precisely where to dig instead of scanning blind.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0c9c20o0tkcl3poxp07y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0c9c20o0tkcl3poxp07y.png" alt="SiteGround View Evidence panel showing 14 suspicious files with full server paths"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;SiteGround’s View Evidence panel: 14 suspicious files, each with a full path. Client paths are redacted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The flagged paths followed an obvious pattern — file names that &lt;em&gt;look&lt;/em&gt; like real WordPress, plugin, and theme files but don’t exist in any clean install:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flagged file&lt;/th&gt;
&lt;th&gt;Where it sat&lt;/th&gt;
&lt;th&gt;What it impersonates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;single-compiler.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-content/themes/woodmart/&lt;/td&gt;
&lt;td&gt;A WooDmart &lt;code&gt;single-*.php&lt;/code&gt; template (the primary backdoor)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;single-portfolio-first.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-content/themes/woodmart/&lt;/td&gt;
&lt;td&gt;WooDmart portfolio template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;embed-merge.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-includes/&lt;/td&gt;
&lt;td&gt;WordPress embed code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;border-call.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-includes/block-supports/&lt;/td&gt;
&lt;td&gt;Core block-supports &lt;code&gt;border.php&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;class-wp-html-processor-url.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-includes/html-api/&lt;/td&gt;
&lt;td&gt;Core &lt;code&gt;class-wp-html-processor.php&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;options-permalink-method.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-admin/&lt;/td&gt;
&lt;td&gt;Core &lt;code&gt;options-permalink.php&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;post-types-ajax-response.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-content/plugins/woodmart-core/&lt;/td&gt;
&lt;td&gt;WooDmart Core internals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;revslider-beta.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-content/plugins/revslider/&lt;/td&gt;
&lt;td&gt;Slider Revolution file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;updraftplus-json.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wp-content/plugins/updraftplus/&lt;/td&gt;
&lt;td&gt;UpdraftPlus file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wp-config-sample.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;public_html/ (root)&lt;/td&gt;
&lt;td&gt;Real WP file — flagged because it had been modified&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Scattering shells across &lt;code&gt;wp-includes&lt;/code&gt;, &lt;code&gt;wp-admin&lt;/code&gt;, the theme, and three different plugins is deliberate. It’s a &lt;strong&gt;distributed backdoor&lt;/strong&gt; : delete one and the attacker still has nine more doors. This is why a “found the bad file, deleted it” approach gets sites re-suspended within days.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the malware actually was: a Tiny File Manager backdoor
&lt;/h2&gt;

&lt;p&gt;The headline file, &lt;code&gt;single-compiler.php&lt;/code&gt;, sat in the WooDmart theme folder at 181 KB with a modification date stomped to match the legitimate theme files around it (a 2020 timestamp), so it blended into a directory listing. WooDmart has no template called &lt;code&gt;single-compiler.php&lt;/code&gt; — the name simply borrows the theme’s &lt;code&gt;single-*.php&lt;/code&gt; convention to look native.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmen5crrvt89837z9sw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmen5crrvt89837z9sw0.png" alt="File manager showing single-compiler.php inside wp-content themes woodmart at 181 KB"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;single-compiler.php in the WooDmart theme directory — 181 KB and a stomped timestamp matching the real templates.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Opening it revealed it was not WordPress code at all. It was a renamed copy of &lt;strong&gt;Tiny File Manager&lt;/strong&gt; (the open-source “H3K / CCP Programmers” single-file PHP file manager), weaponized as a full webshell. Tiny File Manager is a legitimate admin tool, but dropped into a hacked site it gives an attacker a browser-based control panel to browse the entire document root, upload and edit any file, delete files, change permissions, build and extract archives, and pull remote payloads in via its “upload from URL” feature — all without ever touching &lt;code&gt;wp-admin&lt;/code&gt;. The copy here shipped with a hardcoded &lt;code&gt;admin&lt;/code&gt; account and a &lt;code&gt;noindex, nofollow&lt;/code&gt; meta tag so search engines wouldn’t surface it.&lt;/p&gt;

&lt;p&gt;The other 13 files were lighter secondary shells and loaders following the same camouflage logic. Based on the outdated WooDmart Core plugin present on the site (older versions carried a known PHP Object Injection flaw) and the timestamps, the most probable entry point was a vulnerable/outdated theme component or a reused admin credential — not a SiteGround server breach. I cover how persistent shells like this survive a “delete the file” cleanup in &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/i-found-a-hidden-8220backdoor8221-in-a-clients-wordpress-site-5g0m"&gt;this backdoor breakdown&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to confirm a Tiny File Manager backdoor on your own site
&lt;/h2&gt;

&lt;p&gt;If SiteGround (or any host) flags files like these, these commands confirm the infection over SSH before you delete anything. Tiny File Manager carries unmistakable signatures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Hunt for Tiny File Manager signatures anywhere in the site&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"Tiny File Manager"&lt;/span&gt; wp-content/ wp-includes/ wp-admin/
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"CCP Programmers"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rln&lt;/span&gt; &lt;span class="s1"&gt;'\$auth_users'&lt;/span&gt; wp-content/ wp-includes/ wp-admin/
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"FM_SESSION_ID"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Core/plugin files that should not exist will fail checksum verification&lt;/span&gt;
wp core verify-checksums
wp plugin verify-checksums &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="c"&gt;# Oversized or odd PHP hiding in the theme (a 181 KB "template" is a red flag)&lt;/span&gt;
find wp-content/themes/ &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.php"&lt;/span&gt; &lt;span class="nt"&gt;-size&lt;/span&gt; +50k &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
find wp-content/themes/woodmart/ &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"single-*.php"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;wp core verify-checksums&lt;/code&gt; is the fastest way to expose the camouflaged core files — &lt;code&gt;embed-merge.php&lt;/code&gt;, &lt;code&gt;border-call.php&lt;/code&gt;, &lt;code&gt;class-wp-html-processor-url.php&lt;/code&gt; and &lt;code&gt;options-permalink-method.php&lt;/code&gt; all show up as files that don’t belong in a clean WordPress install. For a fuller detection routine, see my guide on &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/how-to-detect-wordpress-malware-before-it-ruins-your-business-49a"&gt;how to detect WordPress malware&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cleanup, step by step
&lt;/h2&gt;

&lt;p&gt;SiteGround keeps the site quarantined while you work, but it gives you tools to operate. Under the Review modal’s &lt;strong&gt;More&lt;/strong&gt; button you’ll find &lt;strong&gt;Whitelist IP&lt;/strong&gt; (so you can reach the site to clean it) and &lt;strong&gt;Request Scan&lt;/strong&gt; (to re-check once you’re done).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cwknr0lk4m4j75utpdd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cwknr0lk4m4j75utpdd.png" alt="SiteGround More dropdown showing Request Scan and Whitelist IP options"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The More menu: Whitelist IP to regain access during cleanup, and Request Scan to trigger a free rescan (up to three per day).&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Whitelist my IP&lt;/strong&gt; via More → Whitelist IP, so I could reach File Manager and the site while everyone else stayed blocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Take a forensic backup first.&lt;/strong&gt; Before deleting anything I copied the flagged files off-server so I had a record of the attack and a safety net.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removed all 14 flagged files&lt;/strong&gt; , then hunted for siblings the scanner missed using the grep and checksum commands above. Host scanners rarely catch 100% — assume there are more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replaced the theme and core from clean sources.&lt;/strong&gt; I deleted the WooDmart folder and reinstalled it from a fresh download, and reinstalled WordPress core, rather than trusting files that had been tampered with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checked the usual hiding spots&lt;/strong&gt; — &lt;code&gt;wp-config.php&lt;/code&gt;, &lt;code&gt;.htaccess&lt;/code&gt;, &lt;code&gt;mu-plugins&lt;/code&gt;, the uploads directory, and &lt;code&gt;wp_users&lt;/code&gt;/&lt;code&gt;wp_options&lt;/code&gt; for rogue admins or injected autoloaded rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hit Request Scan.&lt;/strong&gt; SiteGround re-scanned, returned clean, and the suspension lifted the same day. (Rescans are free, up to three per day.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The broader version of this recovery — including what to do when the host won’t restore access and how to clean over FTP — is in my &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/hosting-account-suspended-for-malware-the-complete-recovery-playbook-bluehost-siteground-1mp2-temp-slug-5940722"&gt;hosting account suspended malware recovery guide&lt;/a&gt;. I documented a similar host-level suspension on a different provider in this &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/bluehost-wordpress-site-hacked-how-i-recovered-an-account-with-1162-infected-files-and-a-403-2fh8-temp-slug-227222"&gt;Bluehost recovery case study&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I re-scanned with Wordfence after SiteGround cleared it
&lt;/h2&gt;

&lt;p&gt;A passing host scan is necessary, not sufficient. SiteGround’s scanner is signature-based and tuned to protect the server, not to give you a forensic all-clear — it sometimes misses lower-signal shells and injected database content. Once the site was back online I installed Wordfence and ran a full scan as an independent second opinion. It confirmed the file system was clean and surfaced nothing new, which is exactly the confirmation you want before you call a site recovered. Two different engines agreeing is far stronger than trusting one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening so the suspension doesn’t repeat
&lt;/h2&gt;

&lt;p&gt;A clean site that keeps the same passwords and the same vulnerable plugins gets reinfected. After the rescan passed, I:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rotated &lt;strong&gt;every&lt;/strong&gt; password — WordPress admins, SiteGround account, FTP/SFTP, database, and email.&lt;/li&gt;
&lt;li&gt;Regenerated the &lt;strong&gt;secret keys and salts&lt;/strong&gt; in &lt;code&gt;wp-config.php&lt;/code&gt; to force-log-out any session an attacker still held.&lt;/li&gt;
&lt;li&gt;Updated WordPress core, the WooDmart theme, and all plugins to current versions, and removed anything nulled or unused.&lt;/li&gt;
&lt;li&gt;Disabled in-dashboard file editing (&lt;code&gt;define('DISALLOW_FILE_EDIT', true);&lt;/code&gt;), enabled two-factor on admin logins, and tightened file permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My full post-cleanup routine is in &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/what-to-do-after-fixing-a-hacked-wordpress-site-the-72-hour-verification-protocol-from-4500-2c8m-temp-slug-7327467"&gt;what to do after fixing a hacked WordPress site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The suspension was lifted the same day the cleanup finished, the storefront came back online, SiteGround’s rescan returned clean, and a follow-up Wordfence scan confirmed no reinfection. Total downtime was a single working session rather than the days a back-and-forth with support typically costs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“My website was suffering from some redirect malware. MD was able to take care of the problem for a reasonable fee. For me, he was a lifesaver. I will certainly go to him first should something like that happen again.” &lt;cite&gt;Kendall Miller, Founder&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  When to get help
&lt;/h2&gt;

&lt;p&gt;If SiteGround has suspended your site and the evidence list looks anything like the one above — disguised PHP files scattered across core, theme, and plugins — the safe move is to remove every shell, not just the obvious one, before you request a rescan. I’ve cleaned &lt;strong&gt;4,500+ hacked WordPress sites&lt;/strong&gt; and lifted host suspensions on SiteGround, Bluehost, Hostinger, and more. If you’d rather not do it yourself, my &lt;a href="https://www.mdpabel.com/wordpress-malware-removal/" rel="noopener noreferrer"&gt;WordPress malware removal service&lt;/a&gt; handles the whole thing — diagnosis, cleanup, rescan, and hardening — or you can &lt;a href="https://www.mdpabel.com/hire-me/" rel="noopener noreferrer"&gt;hire me directly&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why did SiteGround suspend my site for malware?
&lt;/h3&gt;

&lt;p&gt;SiteGround runs automatic malware scans on hosted accounts. When the scanner finds malicious PHP — a webshell, backdoor, or injected loader — it temporarily suspends web access to protect its servers and IP reputation. The site stays quarantined until you remove the flagged files and pass a rescan.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does “Malware detected for your site(s)” mean in SiteGround?
&lt;/h3&gt;

&lt;p&gt;It means SiteGround’s scanner found malicious code in your hosting account and has suspended web access until it’s cleaned. Open the Client Area, click Review on the notification, then View Evidence to see the exact file paths. Remove every listed file (and any siblings), then use Request Scan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is single-compiler.php a real WooDmart theme file?
&lt;/h3&gt;

&lt;p&gt;No. WooDmart has no &lt;code&gt;single-compiler.php&lt;/code&gt; template. The name copies the theme’s legitimate &lt;code&gt;single-*.php&lt;/code&gt; naming so it blends in. In this case it was a renamed Tiny File Manager — an open-source file manager weaponized as a backdoor. If you find it in your theme folder, delete it and scan for more shells.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I get my SiteGround site unsuspended after malware?
&lt;/h3&gt;

&lt;p&gt;Whitelist your IP (Review → More → Whitelist IP) to regain access, remove every file in the View Evidence list plus any hidden siblings, replace tampered core/theme/plugin files from clean copies, then click Request Scan. A clean rescan lifts the suspension, usually within minutes to a few hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does SiteGround’s scanner catch all the malware?
&lt;/h3&gt;

&lt;p&gt;Not always. Host scanners are signature-based and can miss lower-signal shells or database injections. After SiteGround clears your site, run an independent scan with Wordfence or a manual file review. Two engines agreeing — plus a checksum verification of core and plugins — is a far stronger all-clear than one passing scan.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Last updated: April 2026 by &lt;a href="https://www.mdpabel.com/about/" rel="noopener noreferrer"&gt;MD Pabel&lt;/a&gt;, WordPress Security Specialist — 4,500+ sites cleaned.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>VirusTotal Flagged Your Website? What It Really Means — and How to Actually Get Removed</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Sat, 06 Jun 2026 04:04:08 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/virustotal-flagged-your-website-what-it-really-means-and-how-to-actually-get-removed-24eo</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/virustotal-flagged-your-website-what-it-really-means-and-how-to-actually-get-removed-24eo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick answer&lt;/strong&gt; VirusTotal has no blacklist of its own and &lt;strong&gt;cannot add or remove your website&lt;/strong&gt;. It is an aggregator that mirrors the verdicts of 70+ antivirus engines and URL/domain blocklisting services. To clear a flag you fix the underlying problem on your site, then ask each &lt;em&gt;flagging vendor&lt;/em&gt; — not VirusTotal — to re-scan and clear you. A genuinely clean site that is flagged is usually a false positive; a flag that will not go away on a previously hacked WordPress site almost always means leftover malware.&lt;br&gt;
You scanned your domain on VirusTotal, saw something like &lt;em&gt;3 of 94 security vendors flagged this URL as malicious&lt;/em&gt;, and your first instinct was to find the “remove from VirusTotal” button. There isn’t one — and that is not a bug. Understanding why is the difference between wasting a week emailing the wrong people and getting your site cleared in a day or two. I’m MD Pabel, and I’ve cleaned more than 4,500 hacked WordPress sites. A misread VirusTotal report is one of the most common screenshots clients send me, usually with the same question: “How do I get off VirusTotal’s blacklist?” The short version is that VirusTotal doesn’t have one. Here’s what the screen actually means and exactly what to do about it.&lt;/p&gt;
&lt;h2&gt;
  
  
  VirusTotal is not a blacklist. It’s a mirror.
&lt;/h2&gt;

&lt;p&gt;VirusTotal is owned by Google and runs anything you submit — a file, URL, domain, or IP — through more than 70 antivirus engines and URL/domain blocklisting services at once. That’s its entire job: collect verdicts from many independent vendors and show them in one place. The part most people miss is that VirusTotal produces &lt;strong&gt;no verdict of its own&lt;/strong&gt;. It doesn’t decide your site is malicious. It reports what Kaspersky, BitDefender, Sophos, Google Safe Browsing, and the rest are saying about you, and it aggregates those answers into a tidy count. So “VirusTotal blacklisted my site” is technically impossible. What actually happened is that one or more of the engines VirusTotal queries flagged your domain, and VirusTotal is simply displaying their results together. Think of VirusTotal as a scoreboard, not the referee. Shouting at the scoreboard won’t change the score. You have to go to the officials who put the numbers up there.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why you can’t “remove” your site from VirusTotal
&lt;/h2&gt;
&lt;h2&gt;
  
  
  This trips up almost everyone, so it’s worth being blunt. Even in the rare case VirusTotal hid a flag on its own display, the people running the antivirus product that flagged you &lt;strong&gt;would still be blocked from your site&lt;/strong&gt;. The warning lives in the vendor’s database, not in VirusTotal’s. Only the vendor that flagged you can clear it. A couple of other quirks matter:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Domain reports don’t even carry a verdict.&lt;/strong&gt; A VirusTotal &lt;em&gt;domain&lt;/em&gt; page mostly shows relationships and history, not a malicious/clean ruling. The actionable verdicts come from &lt;em&gt;URL&lt;/em&gt; scans.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VirusTotal caches results.&lt;/strong&gt; A site you fixed yesterday can keep showing an old verdict until the specific URL is re-scanned, which is why people panic when their “cleaned” site still looks flagged.&lt;br&gt;
The takeaway: the fix never lives inside VirusTotal. It lives at the source (your website) and at each vendor that flagged you.&lt;/p&gt;
&lt;h2&gt;
  
  
  First, figure out which problem you actually have
&lt;/h2&gt;

&lt;p&gt;Two completely different situations get lumped under “VirusTotal flagged my site,” and they have opposite fixes. Get this wrong and you’ll either waste weeks or, worse, send delisting requests for a site that is still infected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A true false positive.&lt;/strong&gt; Your site is genuinely clean and a vendor’s automated system flagged it by mistake. This is common with brand-new domains, aggressive heuristics, shared-hosting IP reputation, or a legitimate script that loads an unfamiliar external domain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A real, lingering infection.&lt;/strong&gt; Your site was hacked, you (or a plugin) “cleaned” it, but the flag persists because malware is still there or has already come back.
For WordPress sites, the second case is far more common — and it’s the one generic “just report a false positive” articles get dangerously wrong. If your site was hacked, the flag usually isn’t a mistake. It’s the symptom. (If your malware seems to keep returning no matter what you do, read &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/why-wordpress-malware-keeps-coming-back-and-how-to-stop-it-forever-4953"&gt;why WordPress malware keeps coming back&lt;/a&gt;.) Here’s how to tell which situation you’re in before you do anything else:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Likely a false positive&lt;/th&gt;
&lt;th&gt;Likely still infected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Recent history&lt;/td&gt;
&lt;td&gt;No hack, nothing changed&lt;/td&gt;
&lt;td&gt;Site was hacked or you saw symptoms recently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What’s flagged&lt;/td&gt;
&lt;td&gt;Your root domain, no specific bad URL&lt;/td&gt;
&lt;td&gt;A specific path, file, or injected page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which engines&lt;/td&gt;
&lt;td&gt;1–2 obscure engines&lt;/td&gt;
&lt;td&gt;Reputable engines (Google, major AVs), or the count is rising&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The label&lt;/td&gt;
&lt;td&gt;“Suspicious”&lt;/td&gt;
&lt;td&gt;“Malware site,” “Phishing site,” “Malicious”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On your site&lt;/td&gt;
&lt;td&gt;Nothing unusual in files or database&lt;/td&gt;
&lt;td&gt;Unknown files, redirects, spam pages, or new admin users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After re-scan&lt;/td&gt;
&lt;td&gt;Clears, or stays at the same low number&lt;/td&gt;
&lt;td&gt;Comes back or increases&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  How to read your VirusTotal report
&lt;/h2&gt;

&lt;p&gt;To contact the right people, you have to read the report correctly. Three things matter:&lt;/p&gt;
&lt;h3&gt;
  
  
  The detection count is not a severity score
&lt;/h3&gt;

&lt;p&gt;A result of “2 / 94” doesn’t mean your site is 2% bad. A single reputable engine labelling you a &lt;em&gt;malware site&lt;/em&gt; is far more serious than five obscure scanners flagging you. Likewise, “0 / 94” doesn’t guarantee you’re clean — it only means none of the engines on VirusTotal flagged you (more on that below). Treat the count as a starting point, not a grade.&lt;/p&gt;
&lt;h3&gt;
  
  
  The label tells you the type of problem
&lt;/h3&gt;

&lt;p&gt;Each engine returns a category: &lt;em&gt;clean&lt;/em&gt;, &lt;em&gt;unrated&lt;/em&gt; (never reviewed you), &lt;em&gt;suspicious&lt;/em&gt;, &lt;em&gt;malicious&lt;/em&gt;, &lt;em&gt;malware site&lt;/em&gt;, or &lt;em&gt;phishing site&lt;/em&gt;. Write down which engine reported which label. A “phishing site” verdict points to fake login or payment pages; a “malware site” verdict points to injected scripts or drive-by code. The label is your first clue about where to look on your own site.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ignore the Community score
&lt;/h3&gt;

&lt;p&gt;The Community score is voted on by registered VirusTotal users and weighted by their reputation. It can be skewed by anyone with an account and is not a reliable signal. Don’t make decisions based on it. By the end of this step you should have a simple list: &lt;strong&gt;which vendors flagged you, and with what label.&lt;/strong&gt; That list is your to-do list — each vendor has its own delisting process. For the full diagnosis-to-delisting workflow across every blocklist, see my &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/website-blacklisted-the-2026-diagnosis-038-delisting-playbook-from-4500-real-cleanups-1gd-temp-slug-7985374"&gt;website blacklist diagnosis and delisting playbook&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  If your WordPress site is genuinely infected (the usual case)
&lt;/h2&gt;

&lt;p&gt;Security engines flag &lt;em&gt;behavior&lt;/em&gt;, not appearance. Your homepage can look perfect while an injected script, a mobile-only redirect, a hidden spam directory, or a backdoor sits in your files or database. That’s why “but my site looks fine” is never proof of anything. If you’re comfortable on the command line, here are a few quick checks to confirm an infection before you assume it’s a false positive. (The full walkthrough is in my guide on &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/how-to-detect-wordpress-malware-before-it-ruins-your-business-49a"&gt;how to detect WordPress malware&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# PHP files modified in the last 7 days (common after a fresh hack)&lt;/span&gt;
find /path/to/wordpress &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.php"&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-ls&lt;/span&gt;

&lt;span class="c"&gt;# PHP files inside uploads — there should be ZERO&lt;/span&gt;
find /path/to/wordpress/wp-content/uploads &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.php"&lt;/span&gt;

&lt;span class="c"&gt;# Search the database for the most common injection patterns&lt;/span&gt;
wp db search &lt;span class="s1"&gt;'eval('&lt;/span&gt; &lt;span class="nt"&gt;--all-tables&lt;/span&gt;
wp db search &lt;span class="s1"&gt;'base64_decode'&lt;/span&gt; &lt;span class="nt"&gt;--all-tables&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the part that catches people out: a single leftover backdoor re-infects a site within hours, and the next vendor scan re-flags you — sometimes &lt;em&gt;after&lt;/em&gt; you’ve already requested delisting, which makes you look like you’re crying wolf. I once watched a client fail a Google review three times until we traced the reinfection to a payload hidden in the database, not the files (&lt;a href="https://dev.to/md_pabel_fe07e07449db7326/failed-google-blacklist-review-how-i-found-hidden-database-malware-after-3-rejected-cleanup-2i9e-temp-slug-1304255"&gt;that case study is here&lt;/a&gt;). The rule: if you are not confident your site is 100% clean, requesting delisting is premature. You will fail the review and burn time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real removal process, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clean the site completely.&lt;/strong&gt; Every backdoor, injected file, malicious database row, rogue admin user, and poisoned &lt;code&gt;.htaccess&lt;/code&gt; rule. Half-measures are why flags come back. (My &lt;a href="https://www.mdpabel.com/blog/wordpress-malware-removal-expert-guide-to-clean-hacked-wordpress-site/" rel="noopener noreferrer"&gt;expert malware-removal guide&lt;/a&gt; covers the process; the &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/what-to-do-after-fixing-a-hacked-wordpress-site-the-72-hour-verification-protocol-from-4500-2c8m-temp-slug-7327467"&gt;post-cleanup checklist&lt;/a&gt; covers what comes next.)&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify with more than one scanner.&lt;/strong&gt; Don’t trust a single tool. Cross-check with Sucuri SiteCheck, Quttera, and Google’s Safe Browsing status before you declare victory.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Re-scan the flagged URL on VirusTotal.&lt;/strong&gt; This clears VirusTotal’s cached result so it reflects your cleaned site, not the snapshot from when you were hacked.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Contact each flagging vendor directly.&lt;/strong&gt; Use an email address at your own domain (it helps prove ownership), include a link to the VirusTotal report, and attach proof of a clean scan. Submitting from a domain email with clean-scan evidence is the single biggest factor in getting a fast review.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Be patient with propagation.&lt;/strong&gt; Some vendors clear you within hours; others take days, and full propagation across every cache can take up to about 30 days. Don’t re-submit hourly — that doesn’t speed anything up.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Handle the vendors VirusTotal doesn’t show.&lt;/strong&gt; See the next section — this is the step people forget, and it’s why warnings persist in browsers even after VirusTotal reads 0 / 94.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The vendors VirusTotal won’t show you
&lt;/h2&gt;

&lt;h2&gt;
  
  
  VirusTotal covers a lot of ground, but not everything. Several of the warnings that actually scare your visitors away come from sources that may never appear in your VirusTotal report:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Google Safe Browsing&lt;/strong&gt; powers the red “Dangerous site” interstitial in Chrome, Firefox, and Safari — the warning the largest share of your visitors will actually hit. Check your status separately and request a review in Google Search Console under Security Issues. If Google is the problem, see my &lt;a href="https://www.mdpabel.com/google-blacklist-removal-service/" rel="noopener noreferrer"&gt;Google blacklist removal service&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Norton Safe Web&lt;/strong&gt; and similar reputation services often flag sites without showing up on VirusTotal. If Norton is warning on your domain, follow my &lt;a href="https://www.mdpabel.com/blog/norton-blacklist-removal-wordpress-malware-infection-spam-norton-virus-removal-guide/" rel="noopener noreferrer"&gt;Norton blacklist removal guide&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avast and McAfee&lt;/strong&gt; each maintain their own URL reputation systems with their own submission forms. See &lt;a href="https://www.mdpabel.com/avast-blacklist-removal/" rel="noopener noreferrer"&gt;Avast blacklist removal&lt;/a&gt; and &lt;a href="https://www.mdpabel.com/mcafee-blacklist-removal/" rel="noopener noreferrer"&gt;McAfee blacklist removal&lt;/a&gt;.&lt;br&gt;
This is exactly why “0 detections on VirusTotal” doesn’t always mean every warning is gone. A clean VirusTotal report is encouraging, not final.&lt;/p&gt;
&lt;h2&gt;
  
  
  Want this handled instead?
&lt;/h2&gt;

&lt;p&gt;I’ll be honest: cleaning a hacked WordPress site and then chasing down every vendor that flagged you is tedious, and it’s easy to get wrong. The usual failure points are a hidden backdoor that triggers reinfection, a delisting request sent too early, or a warning source (like Google or Norton) that nobody checked. I’ve handled all three across more than 4,500 cleanups.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your site is flagged and you’d rather have a specialist clean it and clear every blocklist for you:&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://www.mdpabel.com/wordpress-malware-removal/" rel="noopener noreferrer"&gt;WordPress malware removal&lt;/a&gt; — full cleanup, root cause fixed → &lt;a href="https://www.mdpabel.com/blacklist-removal/" rel="noopener noreferrer"&gt;Blacklist removal&lt;/a&gt; — I file and follow up with every vendor → &lt;a href="https://www.mdpabel.com/hire-me/" rel="noopener noreferrer"&gt;Hire me&lt;/a&gt; — done-for-you, with results reviewed before you pay&lt;/p&gt;

&lt;h2&gt;
  
  
  What clients say
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“My website was suffering from some redirect malware. MD was able to take care of the problem for a reasonable fee. For me, he was a lifesaver. I will certainly go to him first should something like that happen again.”&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;— Kendall Miller, Founder&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;“I’m very satisfied with MD Pabel’s service. He saved my site from hackers and removed all malware attacks. Highly recommended.”&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;— Hassan Infinkey, eCommerce Owner&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can VirusTotal blacklist my website?
&lt;/h3&gt;

&lt;p&gt;No. VirusTotal maintains no blacklist and issues no verdicts of its own. It aggregates results from 70+ independent antivirus and URL-blocklisting engines. If you appear flagged on VirusTotal, it’s because one or more of those engines flagged you — and only that engine’s owner can remove the flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I remove my website from VirusTotal?
&lt;/h3&gt;

&lt;p&gt;You can’t remove it from VirusTotal directly, because the flag lives with the vendor, not VirusTotal. Fix the underlying issue on your site, re-scan the URL to clear VirusTotal’s cache, then contact each flagging vendor from your domain email with proof of a clean scan and ask them to re-review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is my site still flagged on VirusTotal after I cleaned it?
&lt;/h3&gt;

&lt;p&gt;Three usual reasons: VirusTotal is showing a cached result (re-scan the URL), the flagging vendor hasn’t re-reviewed you yet, or — most often for WordPress — the site isn’t actually clean. A leftover backdoor reinfects within hours and gets you re-flagged. Verify the site is fully clean before assuming it’s a caching delay.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does 1 of 94 detections on VirusTotal mean my site is infected?
&lt;/h3&gt;

&lt;p&gt;Not on its own. A single obscure engine can be a false positive. But if that one detection comes from a reputable vendor and labels you “malware” or “phishing,” take it seriously and investigate. The detection count is a starting point, not a severity score — the engine and the label matter more than the number.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to clear a VirusTotal flag?
&lt;/h3&gt;

&lt;p&gt;It depends on the vendor. Some clear you within a few hours of a confirmed clean review; others take several days. Full propagation across every cache and product can take up to roughly 30 days. Submitting from your domain email with clean-scan evidence is the fastest path.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Last updated: June 6, 2026 by &lt;a href="https://www.mdpabel.com/about/" rel="noopener noreferrer"&gt;MD Pabel&lt;/a&gt;, WordPress Security Specialist — 4,500+ sites cleaned.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>infosec</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Secure WordPress Without Security Plugins</title>
      <dc:creator>MD Pabel</dc:creator>
      <pubDate>Wed, 03 Jun 2026 03:04:23 +0000</pubDate>
      <link>https://dev.to/md_pabel_fe07e07449db7326/how-to-secure-wordpress-without-security-plugins-1j2g</link>
      <guid>https://dev.to/md_pabel_fe07e07449db7326/how-to-secure-wordpress-without-security-plugins-1j2g</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick answer:&lt;/strong&gt; Yes, you can secure WordPress without any security plugins. Most real protection comes from two layers you control directly: &lt;strong&gt;application hardening&lt;/strong&gt; (wp-config.php rules, correct file permissions, disabled file editing) and &lt;strong&gt;server-level controls&lt;/strong&gt; (a web-server firewall, blocked PHP execution, least-privilege database access). Plugins add convenience and monitoring, not the foundation.&lt;/p&gt;

&lt;p&gt;After manually cleaning more than 4,500 hacked WordPress sites, one pattern repeats: the site that gets reinfected almost always &lt;em&gt;had&lt;/em&gt; a security plugin installed, and was breached anyway. The plugin was never the problem. It just was not the foundation.&lt;/p&gt;

&lt;p&gt;The numbers explain why. Patchstack’s 2026 report counted 11,334 new WordPress vulnerabilities in 2025, a 42% jump year over year. Plugins accounted for 91% of them; WordPress core had just two. The weighted median time from public disclosure to first exploitation was five hours. You cannot patch fast enough to win that race. The most reliable thing you can do is shrink what an attacker can reach in the first place, and almost all of that work needs zero plugins.&lt;/p&gt;

&lt;p&gt;This guide is the no-plugin path: the server-level and application-level hardening I apply after every cleanup. If you would rather compare dedicated tools, see my breakdown of the &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/best-wordpress-security-plugins-which-one-should-you-use-in-2026-4j9f-temp-slug-6305155"&gt;best WordPress security plugins&lt;/a&gt;, but read this first, because no plugin replaces the basics below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-level vs. application-level security: the model that matters
&lt;/h2&gt;

&lt;p&gt;Every control on a WordPress site lives at one of two layers, and knowing which is which tells you what you can do without a plugin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application-level security&lt;/strong&gt; runs inside WordPress: PHP, wp-config.php, the database WordPress reads, and the admin dashboard. Every security plugin lives here. So do wp-config rules and file-editing controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-level security&lt;/strong&gt; runs below WordPress: the web server (Apache, Nginx, or LiteSpeed), the PHP runtime, the file system, the MySQL or MariaDB server, and the operating system. A request reaches this layer before WordPress ever loads.&lt;/p&gt;

&lt;p&gt;The difference is &lt;em&gt;when&lt;/em&gt; each layer acts. A server-level firewall can drop a malicious request before a single line of PHP runs, which is cheap and resilient. An application-level plugin only sees a request after WordPress has loaded, which is exactly why malware that gains file access can often switch the plugin off and hide its tracks.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Where it runs&lt;/th&gt;
&lt;th&gt;Good at&lt;/th&gt;
&lt;th&gt;Blind to&lt;/th&gt;
&lt;th&gt;No-plugin tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inside WordPress (PHP)&lt;/td&gt;
&lt;td&gt;Login rules, 2FA, WordPress-specific hardening, change logging&lt;/td&gt;
&lt;td&gt;Anything outside WordPress; can be disabled once files are compromised&lt;/td&gt;
&lt;td&gt;wp-config.php constants, functions.php filters, WP-CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Below WordPress (OS, web server, DB)&lt;/td&gt;
&lt;td&gt;Dropping bad requests early, blocking PHP execution, file integrity, brute-force throttling&lt;/td&gt;
&lt;td&gt;WordPress user authentication; intent of a “valid” request abusing a plugin&lt;/td&gt;
&lt;td&gt;.htaccess / Nginx rules, php.ini, ModSecurity, fail2ban, file permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;But neither layer wins alone. Patchstack tested standard hosting defenses in 2025 and found that 87.8% of real-world WordPress attacks bypassed them, because those attacks abuse WordPress against itself: a vulnerable plugin doing exactly what its own code permits. So the goal is not “server good, plugin bad.” It is: reduce the attack surface first, then harden both layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fewer plugins is itself a security control
&lt;/h2&gt;

&lt;p&gt;Every plugin is third-party code running on your server with broad access to your files and database. With 91% of WordPress vulnerabilities living in plugins, and many of them in abandoned plugins that will never be patched, the math is simple: the less third-party code you run, the less an attacker can target and the less you have to patch inside that five-hour window.&lt;/p&gt;

&lt;p&gt;In nearly every cleanup I do, the entry point is a vulnerable or &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/nulled-wordpress-plugins-amp-themes-the-security-risks-behind-free-premium-tools-a9b-temp-slug-6859103"&gt;nulled plugin or theme&lt;/a&gt;, not WordPress core. So treat plugin reduction as a control in its own right. If a feature can be achieved with a few lines in &lt;code&gt;functions.php&lt;/code&gt; or a single server rule, prefer that over installing a plugin. Audit quarterly, and &lt;em&gt;delete&lt;/em&gt; rather than just deactivate anything unused, because a deactivated plugin still sits on disk and remains reachable. For the broader maintenance routine, see my full guide on &lt;a href="https://www.mdpabel.com/blog/how-to-secure-a-wordpress-site/" rel="noopener noreferrer"&gt;how to secure a WordPress site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application-level hardening (no plugins required)
&lt;/h2&gt;

&lt;p&gt;These controls live in &lt;code&gt;wp-config.php&lt;/code&gt;, your server config, and core WordPress settings. None of them need a plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock down wp-config.php
&lt;/h3&gt;

&lt;p&gt;This file holds your database credentials and secret keys, so it deserves the tightest permission on the site. Set it to &lt;code&gt;600&lt;/code&gt; (or &lt;code&gt;640&lt;/code&gt; if your host requires group read), and if your host allows it, move it one level above the web root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Restrict wp-config.php to the owner only&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 wp-config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disable the built-in file editor
&lt;/h3&gt;

&lt;p&gt;This is the single most useful one-line change I recommend after a cleanup. When an attacker gets one admin session, the first thing they often do is open &lt;strong&gt;Appearance › Theme File Editor&lt;/strong&gt; and paste a backdoor straight into the theme, no FTP needed. I have traced more than one reinfection back to exactly this path, including &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/i-found-a-hidden-8220backdoor8221-in-a-clients-wordpress-site-5g0m"&gt;a hidden backdoor in a client’s site&lt;/a&gt;. Turning the editor off removes that one-click route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In wp-config.php, above the "stop editing" line&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'DISALLOW_FILE_EDIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disable file modifications (advanced)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DISALLOW_FILE_MODS&lt;/code&gt; goes further: it blocks plugin and theme installs, deletions, &lt;em&gt;and&lt;/em&gt; updates from the dashboard. That is powerful, but it has a real trade-off, you also lose automatic updates. Only use it if you patch another way, such as WP-CLI or a deployment pipeline. On a site that is managed by hand and rarely changes, it is one of the strongest anti-tamper controls available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Blocks ALL file changes from the dashboard, including updates&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'DISALLOW_FILE_MODS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Refresh secret keys and force HTTPS in the admin
&lt;/h3&gt;

&lt;p&gt;Your authentication salts sign the cookies that keep you logged in. Regenerating them invalidates every existing session, which is exactly what you want after any suspected breach, it logs out an attacker who may be riding a stolen cookie. Grab a fresh set from the official generator and replace the matching block in &lt;code&gt;wp-config.php&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Generate a fresh set of keys and salts&lt;/span&gt;
&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordpress&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="c1"&gt;// Then force the admin and login over HTTPS&lt;/span&gt;
&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'FORCE_SSL_ADMIN'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Block PHP execution where it does not belong
&lt;/h3&gt;

&lt;p&gt;Most webshells get dropped into &lt;code&gt;wp-content/uploads&lt;/code&gt;, because that folder is writable. Blocking PHP from running there neutralizes the payload even if the file lands on disk, which is one of the highest-value server rules you can add. It is the same principle behind a lot of the &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/wordpress-htaccess-malware-the-complete-guide-to-every-type-with-real-code-samples-4132-temp-slug-5596164"&gt;.htaccess malware&lt;/a&gt; cleanups I handle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apache: place in wp-content/uploads/.htaccess&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Files&lt;/span&gt;&lt;span class="sr"&gt; *.php&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="nc"&gt;Require&lt;/span&gt; &lt;span class="ss"&gt;all&lt;/span&gt; denied
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Files&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c"&gt;# Nginx: add to your server block&lt;/span&gt;
&lt;span class="nc"&gt;location&lt;/span&gt; ~* /wp-content/uploads/.*\.php$ {
  &lt;span class="nc"&gt;deny&lt;/span&gt; all;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shut down XML-RPC if you do not use it
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;xmlrpc.php&lt;/code&gt; is a common target for brute-force amplification and pingback abuse. If you do not use the WordPress mobile app, Jetpack, or an external publishing tool, block the endpoint at the server. Confirm those integrations are not in use first, since this will break them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apache: in the root .htaccess&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Files&lt;/span&gt;&lt;span class="sr"&gt; xmlrpc.php&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="nc"&gt;Require&lt;/span&gt; &lt;span class="ss"&gt;all&lt;/span&gt; denied
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Files&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strip version giveaways and manage application passwords
&lt;/h3&gt;

&lt;p&gt;Remove the WordPress generator tag, delete the default &lt;code&gt;readme.html&lt;/code&gt;, and uninstall unused themes so attackers cannot fingerprint exact versions. Application passwords are another quiet risk: they grant API access and, if leaked, behave like a backdoor. If you do not connect external apps over the REST API, disable them with a one-line filter, no plugin and no Wordfence required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In functions.php or a small must-use plugin&lt;/span&gt;
&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_is_application_passwords_available'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'__return_false'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For login-specific measures beyond this, such as 2FA and protecting the login URL, see my dedicated guide on &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/how-to-secure-wordpress-login-15-practical-ways-to-protect-wp-admin-4m00-temp-slug-7658948"&gt;how to secure WordPress login&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-level hardening (no plugins required)
&lt;/h2&gt;

&lt;p&gt;What you can do here depends on your hosting. On &lt;strong&gt;shared hosting&lt;/strong&gt; you usually have file and .htaccess access but no SSH or php.ini control, so ask your host what is already enabled. On &lt;strong&gt;managed WordPress hosting&lt;/strong&gt; , much of this is configured for you. On a &lt;strong&gt;VPS&lt;/strong&gt; , you control all of it. Apply what your environment allows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set correct file and folder permissions
&lt;/h3&gt;

&lt;p&gt;The standard is &lt;code&gt;755&lt;/code&gt; for directories, &lt;code&gt;644&lt;/code&gt; for files, and &lt;code&gt;600&lt;/code&gt; for &lt;code&gt;wp-config.php&lt;/code&gt;. Never use &lt;code&gt;777&lt;/code&gt; on anything; it lets any process write to that file, which is how a lot of cross-account infections spread on shared servers. If you do not have SSH, you can correct permissions with my &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/how-to-fix-wordpress-file-and-folder-permissions-using-a-simple-php-script-no-ssh-required-2l28"&gt;no-SSH PHP permissions script&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run from your WordPress root over SSH&lt;/span&gt;
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;755 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 wp-config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disable directory browsing
&lt;/h3&gt;

&lt;p&gt;If directory listing is on, anyone can browse your folder structure and read what plugins and backups you have. Turn it off at the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apache: in the root .htaccess&lt;/span&gt;
&lt;span class="nc"&gt;Options&lt;/span&gt; -Indexes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Restrict dangerous PHP functions
&lt;/h3&gt;

&lt;p&gt;Most webshells rely on PHP’s command-execution functions. Disabling the ones a normal WordPress site never needs removes a huge amount of a shell’s capability even if one is uploaded. Test after applying, since a few hosts or plugins may need one of these.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# In php.ini (VPS / managed with access)
&lt;/span&gt;&lt;span class="py"&gt;disable_functions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;exec,shell_exec,system,passthru,popen,proc_open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a server firewall and brute-force blocking
&lt;/h3&gt;

&lt;p&gt;This is the server-level equivalent of what a plugin firewall does, except it acts before PHP loads. On a VPS or capable managed host, run &lt;strong&gt;ModSecurity&lt;/strong&gt; with the OWASP Core Rule Set to filter malicious requests, and &lt;strong&gt;fail2ban&lt;/strong&gt; to ban IPs that hammer &lt;code&gt;wp-login.php&lt;/code&gt;. On shared hosting, ask whether ModSecurity is already active; it often is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock down the database
&lt;/h3&gt;

&lt;p&gt;Give WordPress a dedicated MySQL user scoped to a single database, with a long random password. Do not use the &lt;code&gt;root&lt;/code&gt; account, and never reuse the same database user across multiple sites, that is how one compromised site becomes several. Changing the &lt;code&gt;wp_&lt;/code&gt; table prefix is sometimes suggested too, but it is a low-impact, defense-in-depth measure that can break a site if done carelessly, so I treat it as optional rather than essential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforce HTTPS and security headers at the server
&lt;/h3&gt;

&lt;p&gt;Security headers are pure server config, no plugin needed. They harden the browser side against clickjacking, MIME sniffing, and protocol downgrade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apache: in the root .htaccess&lt;/span&gt;
&lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; X-Content-Type-Options "nosniff"
&lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; X-Frame-Options "SAMEORIGIN"
&lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; Referrer-Policy "strict-origin-when-cross-origin"
&lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; Strict-Transport-Security "max-age=31536000; includeSubDomains"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The one thing hardening cannot replace: knowing when something changed
&lt;/h2&gt;

&lt;p&gt;Hardening shrinks the attack surface. What it does not do is tell you the day a brand-new vulnerability in a plugin you actually use gets weaponized within five hours. That detection-and-virtual-patching gap is the honest, legitimate argument for security plugins. Pretending hardening is invincible is how sites get &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/why-wordpress-malware-keeps-coming-back-and-how-to-stop-it-forever-4953"&gt;reinfected after a cleanup&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can cover the monitoring half without a plugin. WP-CLI verifies your core and plugin files against the official checksums, so any modified or injected file shows up immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Verify core and all plugin files against wordpress.org checksums
&lt;span class="go"&gt;wp core verify-checksums
wp plugin verify-checksums --all
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule those commands with a cron job and you have genuine file-integrity monitoring with zero plugins. The one piece that hardening and checksums still cannot cover is &lt;em&gt;virtual patching&lt;/em&gt; of an unpatched vulnerability inside that five-hour window. For that you genuinely need either a server WAF rule, a managed host that does it for you, or a focused security plugin. Be honest about that single trade-off instead of treating “no plugins” as a complete defense.&lt;/p&gt;

&lt;h2&gt;
  
  
  No-plugin WordPress hardening checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Delete unused plugins and themes; keep everything else updated.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Set permissions: 755 directories, 644 files, 600 for wp-config.php.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;In wp-config: add &lt;code&gt;DISALLOW_FILE_EDIT&lt;/code&gt;, refresh salts, force SSL in admin.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Block PHP execution inside wp-content/uploads.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Disable directory browsing.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Restrict dangerous PHP functions in php.ini.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Scope the database user to one database with a strong password.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Block xmlrpc.php if you do not use it.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Add HTTPS and security headers at the server.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Schedule WP-CLI checksum verification as integrity monitoring.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When to bring in a specialist
&lt;/h2&gt;

&lt;p&gt;One critical caveat: if your site is already infected, do not harden on top of the infection. Locking down a compromised site just locks the attacker’s backdoor in with it. Clean first, then harden. In 4,500-plus cleanups I have seen well-meaning owners apply every step above while a webshell sat untouched in an uploads folder, which is how, for example, this &lt;a href="https://dev.to/md_pabel_fe07e07449db7326/bluehost-wordpress-site-hacked-how-i-recovered-an-account-with-1162-infected-files-and-a-403-2fh8-temp-slug-227222"&gt;Bluehost site recovery&lt;/a&gt; started.&lt;/p&gt;

&lt;p&gt;If your site is currently hacked, redirecting to spam, or blacklisted, my &lt;a href="https://www.mdpabel.com/wordpress-malware-removal/" rel="noopener noreferrer"&gt;WordPress malware removal service&lt;/a&gt; handles the cleanup manually, with no data loss, and applies the hardening in this guide afterward. Prefer to hand the whole thing off? You can &lt;a href="https://www.mdpabel.com/hire-me/" rel="noopener noreferrer"&gt;hire me directly&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can you really secure WordPress without any plugins?
&lt;/h3&gt;

&lt;p&gt;Yes. The core of WordPress security is reducing your attack surface and hardening configuration, and almost all of that happens in wp-config.php, your server settings, and file permissions, none of which need a plugin. The only gap is real-time virtual patching, where a server WAF or focused plugin still helps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I remove my security plugin if my host already has server-level security?
&lt;/h3&gt;

&lt;p&gt;Often yes, if your managed host runs a server WAF, malware scanning, and brute-force protection, a heavy all-in-one plugin is largely redundant and only adds load. But server-level tools cannot show WordPress login activity or enforce 2FA, so keep an application-level option for those specific needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between server-level and application-level security?
&lt;/h3&gt;

&lt;p&gt;Server-level security runs below WordPress (web server, PHP, database, OS) and acts on a request before WordPress loads. Application-level security runs inside WordPress as PHP and only sees a request after it loads. Server-level is harder to disable; application-level is better at WordPress-specific rules. Strong sites use both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does disabling file editing in wp-config actually stop hackers?
&lt;/h3&gt;

&lt;p&gt;It closes one specific, very common path. Attackers who steal an admin session frequently use the built-in theme and plugin editor to paste a backdoor instantly. Setting &lt;code&gt;DISALLOW_FILE_EDIT&lt;/code&gt; removes that one-click option, so they must find another route. It is not a complete defense, but it is a high-value one-line change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is changing the WordPress database prefix worth it for security?
&lt;/h3&gt;

&lt;p&gt;Only marginally. Renaming the &lt;code&gt;wp_&lt;/code&gt; prefix can frustrate some automated attacks that assume default table names, but it does not stop a determined attacker and can break your site if done incorrectly. Treat it as optional defense-in-depth, well after permissions, file-edit lockdown, and PHP-execution blocking.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Last updated: June 3, 2026 by &lt;a href="https://www.mdpabel.com/about/" rel="noopener noreferrer"&gt;MD Pabel&lt;/a&gt;, WordPress Security Specialist, 4,500+ sites cleaned.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>tutorial</category>
      <category>wordpress</category>
    </item>
  </channel>
</rss>
