WEBSITE MAINTENANCE · A REAL CASE
A WordPress Website Was Hacked. Here’s What I Found.
A real WordPress cleanup: how manual monitoring led to an investigation, what the completed checks established, and why the final test followed an enquiry to its recipient.

A WordPress website can load normally while serving code its owner never approved. In this case, a hidden plugin inserted an external script into public pages, additional unauthorized files were scattered through the installation, and the security scanner failed before checking anything.
I first noticed the anomaly during my manual monitoring of the client's website and investigated further. The client did not suffer a financial loss from this incident. The Google Analytics report about the unusual activity arrived roughly a day after I had made the fixes.
I combine automated checks with personal review of my clients' sites. In this case, I spotted the suspicious activity before the automated tools or the AI assistant with site access brought it to my attention. That observation started the investigation described below.
During the response work documented on September 15–16, I isolated the injection source, quarantined 37 individual files, restored components from trusted packages where available, and got the scanner working. I also submitted a test enquiry and confirmed with the owner that it arrived.
The practical question for a business owner is what evidence supports putting the website back to work. Here, that required inspecting public responses, comparing files with trusted originals, completing scans and testing the enquiry route. Each check answered a different part of the question.
It also shows why choosing a proven developer and arranging ongoing technical support belong in the website budget from the start. Without a qualified specialist monitoring the site and investigating alerts, a compromise can go unnoticed. Depending on what the attacker does, the consequences can include lost enquiries, wasted advertising spend, recovery costs and damage to customer trust.
The client and identifying infrastructure details are withheld. Results below describe the evidence available at the end of the documented work.
Search data helped investigate the anomaly I had noticed
An unrelated spam query had generated 4,979 impressions and two clicks in the three-month Google Search Console report I reviewed. Those impressions were concentrated on two consecutive days near the end of August. The September dates I checked showed no impressions for that exact query.
That gave me something specific to investigate. It did not establish when the compromise began or ended.
I searched exported content covering 232 posts and 34 pages, plus 873 URLs from five sitemap files. The selected spam terms were absent. Yet the same unauthorized external script appeared in the HTML returned by eight sampled public pages.
WordPress was adding the code as it generated the response. Searching saved page text could not detect everything being delivered to visitors.
Google's own guidance describes code injection into both HTML and the files that generate pages. It also explains that malicious behaviour can depend on the visitor's browsing context. That supports checking what the application actually serves, alongside its saved content and search reports. Google Search Console: Security Issues
For this incident, I established active injection. A connection to a particular ranking loss remained unproven.
The plugin concealed itself from the dashboard
The component called itself Site Assets, a name that could pass for a routine website utility.
Its code hid it from the normal plugin list, suppressed update information and inserted an external script into page output. I could connect the injection mechanism inside the active plugin with the script tag in public responses.
This matters when checking a compromised WordPress site: the dashboard's plugin list can be incomplete. The investigation needs to compare the interface with files on disk and the application's recorded configuration.
I did not deliberately execute the remote script. Its full behaviour, including any effect on individual visitors, remained outside what I could establish.
The first containment attempt was reversed by WordPress
My first attempt used the WordPress plugin editor to stop the confirmed component from running while preserving its code. WordPress could not communicate back with the website to check for fatal errors, rejected the edit and reverted it.
I requested the homepage again. The script was still present.
The work then moved to server-level access. I preserved the plugin and moved it outside the public website directory. Fresh responses from four sampled pages no longer contained the known script.
That external check determined whether containment had worked. An action inside the editor was only an attempted change until the public response confirmed its effect.
Further inspection found files beyond the visible injection
Removing the loader addressed the observed script. The file review still had to account for ways an attacker might retain access.
I found unauthorized or suspicious files in the website root, core directories, uploads and inactive themes. Some contained PHP behind image or archive extensions. A repository plugin had a modified main file and extra files absent from its official distribution. Other code included backdoor or file-management functionality.
Across the documented quarantine stages, 37 individual files were moved out of the working site, in addition to the hidden plugin and preserved component copies. That count describes the artifacts handled; it is not a count of separate attacks.
Original files were retained with records of their paths, sizes, modification times and hashes. I also exported the database and archived the website files before broad restoration.
There was a limitation in that preservation work: the archive came from a live site after initial containment, and some files moved into quarantine while archiving continued. Their originals were saved separately. The archive was useful recovery evidence, but it was not a consistent snapshot of the entire environment at one moment.
For another incident, capture a consistent snapshot and protect an off-server evidence copy as early as practical. An archive taken from a compromised website also needs inspection before anyone treats it as a safe restoration source.
Trusted originals determined what I could verify
I preserved the altered repository plugin and restored it from its official package. When a later scan flagged it as abandoned, the owner removed it. Three inactive standard WordPress themes were restored from official archives and subsequently updated.
WordPress core passed official checksum verification. After the maintenance changes, all 15 remaining repository plugins passed strict official checksum verification.
A checksum comparison tests whether files agree with a reference. WP-CLI provides separate checks for WordPress core and repository plugins. These checks have defined scopes: a successful plugin check does not validate the database, a commercial theme or the operating system.
The commercial theme illustrated why the reference matters. Of its 1,112 installed files, 1,111 matched the owner's downloaded marketplace package. The remaining file contained the matching original followed by a custom contact-form spam filter, which I reviewed.
However, the downloaded package already contained a license-status modification. It could establish consistency with the purchased ZIP, but could not serve as an independent clean developer release. I did not establish that the theme or its seller caused the intrusion.
Keeping access to official installation packages makes this part of recovery more straightforward. A useful comparison must also account for extra files left behind after a directory is overwritten.
The scanner needed repair before its results meant anything
At one stage, Wordfence displayed a failed scan stage, zero checked objects and zero findings. The zero findings reflected a scan that had failed to do its work.
Diagnostics showed a request back to the site's own domain receiving an HTTP 403 response and a Cloudflare browser challenge. That background request could not complete the interactive check.
A narrowly scoped exception allowed the diagnostic POST request, and the connectivity test passed. The actual scan still failed to start properly.
I inspected the installed official Wordfence code. In that version, the connectivity test used POST while the scan launch used GET with scan-specific parameters. The diagnostic and the real operation were taking different paths.
With the owner's approval, the rule was adjusted around the verified server source, exact host, endpoint and required request conditions. Wordfence still had to authenticate the scan request. I then observed advancing counters, new log entries and a completion record.
Wordfence's documentation identifies blocked server connections and restricted access to the WordPress AJAX handler as possible causes of scan failure. The particular POST/GET difference above came from my inspection of the installed version. Wordfence: Scan Troubleshooting
The completed checks had specific boundaries
The first completed Wordfence scan covered 86,239 files and took 49 minutes and 30 seconds. At review, its current results contained no malware findings and four open maintenance warnings: an abandoned plugin and three theme updates. There were no ignored findings.
The owner removed the abandoned plugin, and the themes were updated. A further scan began after those changes but was still running at the evidence cutoff. Its interim results are not presented here as a completed check.
The scanner used the Community signature feed; its interface reported a 30-day delay. I therefore considered its results alongside manual inspection and integrity checks.
| Evidence | What it established |
|---|---|
| Four fresh public-page responses lacked the known script | The observed indicator was absent from those samples after quarantine |
| Core and 15 remaining repository plugins passed checksums | Covered files agreed with their official references |
| A static pass inspected 13,634 candidate files | The selected checks completed without read errors; their signatures and file-size limits still applied |
| Wordfence completed a scan | The scanner had run; the reviewed result list had the findings described above |
| The owner received the test enquiry | The tested form and delivery route worked after the changes |
I also tightened two infrastructure controls: blocking PHP-like requests under uploads and restricting an old direct HTTP route to the server control panel. External tests confirmed the tested uploads route was blocked and direct IPv4 panel access stopped responding while HTTPS remained reachable. Reboot persistence and separate external IPv6 verification were not demonstrated during the session.
The final business check followed the enquiry to its recipient
After the cleanup and maintenance, I checked the homepage, a service page and the contact page. I submitted one authorized enquiry containing the message “Test.”
The form displayed its success message and cleared its fields. The owner then confirmed receipt.
Those were two separate observations. The form response established that submission had progressed through the interface; the owner's confirmation established that the tested enquiry reached its destination.
For a business relying on website enquiries, this belongs in the acceptance criteria for maintenance work. Different forms or integrations need their own checks.
Professional development and support have a defined scope
This site had been professionally built, with hosting and protective controls configured. Cloudflare and a security plugin were already in place, and an AI assistant had access. The incident still happened. My manual monitoring identified the anomaly, and the Google Analytics report arrived roughly a day after the fixes.
For me, this is a reason to take the whole website setup seriously. Security measures reduce risk; ongoing oversight gives someone the responsibility to notice unusual behaviour, investigate it and respond. Preparation also gives the business a path to recovery.
When a business compares a website quote of $1,000 or less with a more comprehensive project, I want it to compare the actual work and continuing responsibilities:
- Development: the design, implementation, mobile usability and testing of forms and integrations.
- Hosting and configuration: a suitable hosting environment, controlled access and clearly assigned administration responsibilities.
- Protection and recovery: trusted software sources, security configuration, protected backups and a process for checking restoration.
- Ongoing support: updates, monitoring, investigation of alerts, tests after changes and an agreed response process.
These require time, knowledge and continuing work. Ask the provider to distinguish the build price from hosting fees, software licences and ongoing support. A price alone tells you very little about what is covered or who takes responsibility after launch.
That is why I treat professional website development as an investment with a defined scope. Before choosing a quote, ask what you receive, what remains your responsibility and what happens if the site is compromised.
In this case, even the scanner needed investigation before it could finish its work. AI and security tools can help, but they do not replace the professional judgement needed to recognise a problem, decide what to inspect and verify the outcome.
WordPress's security guidance covers trusted software sources, updates, backups and monitoring. These are ongoing risk-reduction measures; hosting and application responsibilities need to be clearly assigned. WordPress: Hardening
The client did not suffer a financial loss in this incident. For a business whose compromise goes unnoticed, lost enquiries, wasted advertising spend, recovery costs and damage to customer trust remain possible consequences. Professional preparation and active support are intended to reduce that exposure and improve the ability to detect and recover from problems.
When this recovery approach needs a wider investigation
This case describes application cleanup and selected infrastructure checks. A business facing suspected customer-data access, repeated reinfection or an environment whose integrity cannot be established needs a wider incident investigation. Recovery may require rebuilding from trusted sources.
Here, the original entry point, complete remote-script behaviour and possible access to private data remained unresolved. The record also did not establish completion of every credential rotation or a forensic examination of the full server.
Those open items determine follow-up work. They should remain visible in the handover even when the identified injection is removed and the enquiry form works.
What a business owner should ask for in the handover
A useful cleanup report should let the owner answer five questions:
- What unauthorized code or behaviour was found, and where?
- What was isolated or restored, and which originals were preserved?
- Which checks completed, and what did they cover?
- Did the website's enquiry or purchase route work through to its destination?
- What remains unresolved, and who is responsible for following it up?
In this case, the documented outcome was removal of the identified injection source, quarantine of the recorded artifacts, verified restoration where trusted references were available, a functioning scanner and confirmed delivery of a test enquiry.
For local businesses, website maintenance and marketing meet at that last step: the website has to carry a visitor's enquiry all the way to the business.
Planning work on your website, Local SEO or AI visibility? Visit Koliesnikov.com to discuss your website and business goals. If you are investigating unusual search activity, keep the affected URLs, dates and examples available for the initial conversation.
About the author: Oleksandr Koliesnikov is a Toronto-based specialist in AI Visibility, GEO, Local SEO and digital marketing who personally works on projects for local businesses in Canada and the United States.

