Your site hands out a valid username to anyone who asks. Closing that is an afternoon — keeping it closed is the job.
The username part is real and it is the worst thing the free checker finds. Two routes give away a login name, and once an attacker has one they stop guessing usernames and only guess passwords. But closing it is an afternoon, the instructions are free, and three months later a plugin auto-updates and something opens again — and nothing that scans you today can tell you it was ever closed.
Run the free checker first. It costs nothing, needs no email address, and it prints everything below with the evidence.
Almost everything on this subject is free, including the fix, including mine.
WordPress security is the most crowded free market anything on this site competes in. Listing it honestly first is the only way the rest of the page means anything.
- My own WordPress security check — every finding, free and ungated
- The checker reads the public surface and prints all six finding ids, with the evidence and the fix, plus the checks that passed. No account, no email address, nothing held back, no capped list. If you have not run it, run it before reading further — it may come back clean, and that is a result worth having for nothing.
- My own engagement builder — the whole hardening scope, with hours on it
- The builder prints a WordPress hardening workstream for free, with a deliverable list and an hour band: exposure closed, the login path moved with rate limiting in front of it, and an update and backup routine. That is the obvious version of this service, already on this site, for nothing. It is the reason this page is not about closing the findings.
- Wordfence, free plugin
- Firewall, brute-force limiting, two-factor, XML-RPC control and a malware scanner, on the box, for free. Install it. This page does not resell it and does not pretend it is not good. The plugin listing ↗
- Sucuri SiteCheck, WPScan and every other remote scanner
-
A free remote read of what your site publishes. WPScan will also enumerate users, plugins
and versions, with
Up to 25 API requests per day are given free of charge, that should be suitable to scan most WordPress websites at least once per day
. Its documentation ↗ - A language model
-
A
wp-config.phphardening block, an.htaccessdeny rule, a Wordfence configuration and a clear explanation of every finding — in seconds, written better than most agency documentation. That is not a competitor this page argues with. It is simply true, and any service built on writing that document was never worth buying.
So the honest position is that closing your findings is not worth paying for. The findings are free, the fix is free with hours attached, and four of the five are one config edit. If that is all you need, take it and go — you have saved a fee and I would rather say so than take it.
Every one of those reads your site once, at the moment you ask.
Which is fine, until you need to know whether today is different from last month.
A scan is an instant. Hardening is every instant nobody scanned. The fault that actually costs a WordPress owner is almost never in today’s report — it is in the gap between reports, and three things make that gap unreachable by anything above.
One: it has to be caused, and the cause is completely ordinary
- WordPress decays faster than a server does, and not through neglect. Plugins auto-update. Somebody installs a page builder. A theme edit puts the generator tag back. One update pulls in a dependency with a problem.
- None of that is a mistake. It is the site being used the way it is meant to be used, and the state worth catching does not exist until it happens.
Two: you cannot detect drift without a baseline taken before the drift
- No scan run today can produce yesterday’s. Exposed today is what every free scanner reports. Clean last month, exposed today is a different sentence, and it is the only evidence that separates broken into from born insecure.
- You can only have that sentence if somebody recorded the exposure surface, the plugin and theme inventory, the accounts and a file reference before the plugin auto-updated and before the account appeared. Run the snapshot now and it is already too late to describe last month.
- This is the one deliverable on this site that cannot be bought back afterwards at any price. Not because of how it is sold — because of what it is.
Three: the outside cannot see the inside, and the market leader says so
- A remote scan reads what the site publishes. A backdoor in an upload, a modified
wp-config.php, an administrator added last night, a scheduled task writing spam at three in the morning — none of it is visible from a browser. - That includes my own checker, which does no path guessing at all: it reads
/wp-json/wp/v2/users,/?author=1,xmlrpc.php,wp-login.phpand an asset address the page itself named. Nothing else. - The baseline is taken inside the install, which is the place no free scanner reaches and the reason it needs your cooperation rather than just your URL.
The vendors say it about their own products, so I do not have to.
Every quote below is from the people selling the thing being described, re-opened on 22 August 2026.
-
// SUCURI, ON ITS OWN FREE REMOTE SCANNER
Since the remote scanner only has access to what’s visible on the browser level, it will not detect anything on the server-side.
And, in the same disclaimer,Remote scanners have limited access and results are not guaranteed.
That is the largest name in WordPress remote scanning saying the outside view is partial. It is not a criticism of their product — it is true of every remote scanner including mine, and it is the whole argument for taking the baseline from inside. The sentences sit as a disclaimer beneath the scan results rather than as a headline, which is worth knowing before you go looking for them. SiteCheck ↗ -
// WORDFENCE, ON THE DIFFERENCE BETWEEN ITS FREE AND PAID FEED
Real-time firewall rule and malware signature [Premium] updates via the Threat Defense Feed (free version is delayed by 30 days).
A site on the free plugin is, by the vendor’s own statement, thirty days behind on exactly the freshly-disclosed problems that are being actively scanned for. That is how they tier it; there is no figure on this page and there is not going to be one. The monthly diff closes that window by hand, for the specific plugins on one specific site, which is a different job from a signature feed rather than a cheaper one. The plugin listing ↗ -
// WORDPRESS CORE, ON THE ONE-LINE XML-RPC FIX EVERYONE IS GIVEN
Contrary to the way it’s named, this filter does not control whether XML-RPC is fully enabled, rather, it only controls whether XML-RPC methods requiring authentication–such as for publishing purposes–are enabled.
And:Further, the filter does not control whether pingbacks or other custom endpoints that don’t require authentication are enabled.
So the snippet a model hands you for the XML-RPC finding does not close the endpoint the checker flagged. WordPress says so in its own hook reference, and it agrees with my own module, which tells you toCheck what depends on it, then block it at the web server
rather than to add the filter. The hook reference ↗ -
// WPSCAN, IN ITS OWN SOURCE, ON WHAT IS ALREADY FIXED
Multicall will only work against WP < 4.4
The help text for its password-attack option. This one cuts against the alarmist version of the XML-RPC finding, which is why it is here. The famous XML-RPC trick — packing hundreds of login attempts into one request — was fixed in core more than a decade ago, and the scanner most likely to be pointed at your site says so itself. The endpoint is still a second front door worth closing. It is not the emergency some pages make it. The source ↗
Five things, and only one of them should worry you today.
Six finding ids, five distinct faults — the first is one fault reached by two routes, and the free checker only clears it when both are closed. Nothing here is critical, and this page is not going to invent something that is.
- wp.rest_users_open + wp.author_enumHigh A valid login name is published
- This is the one worth being alarmed about, and it is the only one. Two different routes hand out the same thing, and the module treats them as a pair for a reason: closing the REST listing while leaving author URLs open changes nothing. In its own words, that is “half of every login handed over for free” — an attacker stops guessing usernames and only guesses passwords, and a brute-force run against a known account is a different proposition from one against an unknown one. Two ids, one fault, one repair.
- wp.version_exposedWarn The core version is readable
- When a vulnerability is disclosed, mass scanning starts within hours and works by reading this number off millions of sites and keeping the ones that match. The module is careful here and so is this page: “Hiding it does not patch anything, and it does take you out of that list, which is the window that matters between a disclosure and your update.” Both halves are true. Hiding it buys you the window; only updating closes it.
- wp.xmlrpc_openWarn The old XML-RPC interface answers
- A second authentication endpoint sitting outside whatever protects your login page — so a plugin counting failed attempts on
wp-login.phpis, in the module’s words, “frequently not watching this at all”. The one-line filter everyone reaches for does not close it, and WordPress says so itself, below. The module’s remediation is “Check what depends on it, then block it at the web server” — and the checking half is the work, because Jetpack and the mobile apps use it. - wp.login_defaultInfo The login page is at the default address
- Information rather than a fault, and the module explains why: “Moving the page is not security on its own — anyone determined finds it again — but it removes almost all of the background noise, which makes a real attempt visible instead of buried.” What actually holds is rate limiting on failed attempts and two-factor for anyone who can publish. A page that sold you a moved login path as security would be selling you the decoration.
- wp.plugins_versionedInfo Plugin and theme versions are readable
- The same matching exercise as the core version, done against plugins — and, as the module puts it, “plugins, not core, are where most WordPress compromises start”. It is information rather than a fault because “removing the version breaks cache busting, so the real answer is keeping them current and removing the ones you no longer use”. That sentence is the retainer in miniature, and it is why this page does not sell you a plugin that hides version numbers.
Four of those five are soft, and saying so is the point rather than a weakness. Two are rated information by my own checker and it explains why in each case. That is exactly why this page is not sold on closing them: they are free to find, mostly cheap to fix, and being closed today says nothing about November. The one that is genuinely serious — the published login name — is also free to find and free to fix.
Two things the checker finds are deliberately not on this page. PHP errors printing onto live pages and a browsable uploads folder are server configuration, and the module routes them to server hardening at the point it raises them — that split is in the code and it existed before this page did. Whether the box is configured safely is that page’s question. Whether the application gives itself away is this one’s.
One dated record of what your site was, and one monthly comparison against it.
The figures below are an example of the shape rather than anybody’s real site — but the rows are the real rows, and the bottom half of the first table cannot be filled in by anything that reads you from outside.
A. The baseline
| Recorded | Seen from | State on the day | Note |
|---|---|---|---|
| REST user listing | Outside | Open — 4 accounts returned | /wp-json/wp/v2/users answered an unauthenticated request. The names are recorded in your copy and nowhere else. |
| Author URL enumeration | Outside | Open — redirects to a login name | /?author=1 redirects to an archive whose address is built from the account’s login name. Closing the REST route alone does not close this one. |
| Core version | Outside | Readable | From the generator tag and from version query strings on core assets. |
| xmlrpc.php | Outside | Reachable | Answers on the default path. Recorded with what currently depends on it, which is the question that decides whether it can be blocked. |
| Login page | Outside | Default path | Recorded with whether anything is rate-limiting it, which is the part that matters more than the path. |
| Component versions in asset paths | Outside | Readable — 23 components named | Every stylesheet and script carries its component name and version in the address. |
| Plugin and theme inventory | Inside | 31 plugins, 2 themes, versions recorded | Not 23. The eight the front page never loads are invisible from outside and are exactly the ones nobody remembers installing. Two are no longer in the plugin directory at all. |
| Accounts that can publish or administer | Inside | 6 — 3 administrators | With last-login dates and which have two-factor. One administrator account belongs to an agency that finished work two years ago. |
| Core and wp-content file reference | Inside | Recorded — core matches upstream | A checksum reference so next month’s run can name the files that changed. This month it establishes the reference; it does not prove anything on its own. |
| Scheduled tasks | Inside | 14 recorded | What runs, how often, and which plugin owns it — so a new one appearing next month is a line in the diff rather than a thing nobody notices. |
Look at the two inventory numbers. The outside sees 23 components because 23 of them load on the front page. The install has 31 plugins and 2 themes. The eight nobody can see from outside are the ones nobody remembers installing, and two of them are no longer in the plugin directory at all — which means no update is ever coming for them. That gap is the single clearest illustration of why a remote scan is not a baseline.
The file reference proves nothing in month one, and the page says so rather than dressing it up. It establishes what the files were. It becomes evidence in month two, when something has changed and the record can name which file and when. A baseline is worth what the next comparison makes it worth.
B. One month of the diff
Three rows, not the whole month. They come from three different places on purpose — the site changed by itself, the world changed, and one change was ours and got reverted by somebody working on something else.
| What changed | Came from | What the comparison showed | Decision |
|---|---|---|---|
| A plugin auto-updated, and the update re-added version query strings to its assets | The site | The component version was readable again on the front end. Nobody did anything wrong. The plugin updated itself overnight, which is what it is supposed to do, and the update shipped its own asset registration. | Re-applied the filter and pinned it to survive the next update. Five minutes. The value is not the five minutes — it is that anyone knew. |
| An authenticated arbitrary-file-read was disclosed in a plugin on the inventory | The world | Published this month, and the installed version is inside the affected range. This is the row that needs the inventory — a generic feed says a plugin has a problem; this says you run it, at this version, on this site. | Updated on a copy, checkout and the booking form exercised, then rolled out. Two hours, most of it verifying, because this site takes money and an update that breaks checkout is a revenue outage. |
| A theme edit re-enabled the generator tag closed two months ago | Us | A developer working on the front end restored a header template from version control and took the hardening out with it. The last known-good says exactly when it was closed and exactly when it came back. | Moved the change out of the theme so a template restore cannot undo it again. The repair is the interesting part: the same fault twice means the fix was in the wrong place. |
The third row is the one worth arguing about, so it is on the page rather than left out. A monthly report that only ever finds other people’s mistakes is a sales document. This one caught my own hardening being undone by a routine template restore, two months after it was applied — and the useful part was not re-applying it. It was learning that the fix had been in the wrong place all along.
The decision column is the deliverable, not the list of changes. Four answers only: update now, mitigate at the web server until an update is safe, disable the feature, or accept it and write down why. Where something was updated, it says whether it was applied to a copy and verified first. “A plugin you run had something disclosed this month, and here is what was done about it” is the whole product.
What is free here, and what is not
Once, in one place, rather than a locked row beside every finding.
Free, always, with no account and no email address
- Every finding from the WordPress checker — by id, by name, with the evidence that proves it. Uncapped, unblurred, no teaser.
- The checks that passed, so the report reads as a report rather than a sales pitch.
- The fix for every one of them, and the explanation of why it matters, on the checker’s own page.
- The entire WordPress hardening scope from the engagement builder — the deliverable list, the hour band and the PDF.
- Everything on this page, including both tables above.
What you would be paying for
- The baseline taken inside the install — the inventory, the accounts, the scheduled tasks and the file reference, dated. The half no scanner reaches.
- The monthly comparison against it, which needs a prior state and an interval and therefore cannot be produced on demand.
- Disclosures matched against your actual inventory rather than a generic feed — the difference between a plugin has a problem and you run it, at this version.
- Updates applied on a copy and verified before they go live, with the verification written down.
That list is the same whether the checker comes back clean or covered in findings. A site with all six checks passing today still has no baseline, and its plugins are still one auto-update away from re-opening one of them. A page that only offered you something when it found a problem would be selling alarm rather than work.
And if access cannot be arranged, that is a check that did not run — not a tier you failed to buy. A baseline needs read access to the install or an export of the inventory and the user list. Without one of those, the inside half is simply unknown, it is reported as unknown, and nothing pretends otherwise.
This asks to touch a live site that takes money, so the limits are in writing.
On a transactional WordPress site the update is the most dangerous thing anyone does. Anything asking for that access should say what it will and will not do before you have to ask.
Nothing is written to your live site by default, and the baseline never writes at all. Taking the snapshot is a read. It records the previous state verbatim before any hardening is even proposed.
- Updates and changes go onto a copy first, get verified there, and then roll out with a way back. Never straight onto the running site. This is the default, not an upgrade.
- You can also just apply each change yourself while I measure and verify. Same record, same evidence, no credential moves. If you will not hand admin access to a live store to anybody, that is a reasonable position and it does not put this out of reach.
- Nothing is deleted. Not a plugin, not a user, not a file. Anything that looks removable is flagged and decided with you, never removed on my judgement.
- A backup taken by you, verified by you, before any change. This page does not sell the backup and does not vouch for it — that is the resilience review, and it is a different piece of work.
- Anything that must not be touched is named before anything starts — a custom plugin, a pinned version, an integration that breaks on update.
This does not claim to detect compromise, only exposure and drift. If the baseline or a later comparison turns up something that looks like a live compromise, that is an incident and a handoff, said plainly on the day. It is not a tier and it is not an upsell.
Working directly on the live site, without a copy in between, is something a client can ask for. It is never the default and never on a first engagement.
Four ways in, and the last one is the point
Everyone starts with the baseline, because until there is a known-good nothing after it can be measured — including by me.
Exposure Baseline
Everyone starts here. It also scopes everything else.
- The public surface read against all six finding ids — and what is already correct, not only what is not
- The baseline taken inside the install: plugin and theme inventory
with versions, the accounts that can publish or administer, scheduled tasks, and a
file reference for core and
wp-content - Dated and recorded, because this is the one artefact that cannot be taken retroactively
- Nothing is changed. The site ends the engagement exactly as it started
- Read access to the install, or I work from an inventory and user list you export
Baseline + Repair
Not a starting point. Follows the baseline.
The tier where the exposures actually close.
- All five faults closed — enumeration by both routes, the version
tag, component versions,
xmlrpc.phpblocked at the web server if nothing needs it, and rate limiting in front of the login - Applied on a copy and verified before it goes live, or applied by you while I verify. Never straight onto the running site
- Two-factor for everyone who can publish
- The baseline re-taken afterwards, so the closing is a recorded change rather than a claim
Baseline fee credited — you pay the difference, not both.
Hardening Baseline
Not a starting point. Follows the baseline.
Changing the shape rather than patching it.
- Everything in the repair
- Rate limiting that covers XML-RPC as well as the login page —
my own checker notes that a plugin counting failed attempts on
wp-login.phpis frequently not watching the other one - Dormant and abandoned components dealt with — the ones with no upstream fix mitigated at the web server rather than left
- An update-and-rollback path proven once, on your site, before it is needed — the safe way to apply the dangerous update, demonstrated
- Handover, so your team can read the baseline and run the comparison without me
Baseline fee credited — you pay the difference, not both.
Hardening Retainer
Monthly. Because a site hardened in August is not hardened in November.
- The monthly comparison: what changed against the last known-good, and which disclosures published this month touch a plugin or theme you actually run
- Each change decided — update now, mitigate at the web server, disable, or accept and record why
- Where something was updated, applied on a copy and verified before it went live
- The baseline re-taken each month, so drift is a recorded number rather than a feeling
- A one-page record each month you can hand to anyone who asks
- Monthly in advance, cancel anytime, never auto-renewing
Five things about the packages, before you ask
- The cap is one site, one WordPress install, one admin surface. That is the unit the baseline and the comparison actually run on. A multisite network is more than one admin surface and more than one inventory, so it is honestly a bigger month and I will say so before quoting rather than after.
- The retainer is the marked tier, and it is the only one this page is really about. Not because it is the biggest — because the one-off closing is free from my own engagement builder, so no one-off tier can honestly be the value here. The tiers before it exist to make the retainer possible: you cannot diff against a baseline you never took.
- What moves the size of the job is danger, not plugin count. In order: whether the site is transactional — a store, a membership, a booking system — because an update that breaks checkout is a revenue outage and every update then runs on a copy first; how large and how old the plugin and theme inventory is; custom or abandoned components with no upstream fix; a page builder or a heavily customised theme, where an update is most likely to break the front end; multisite; and whether you hand over access or apply changes yourself while I measure.
- The baseline fee is credited for 30 days. Come back inside a month and you pay the difference. After that it lapses, and not as a sales tactic: plugins update, and a baseline from last quarter would have to be re-taken anyway.
- The retainer never auto-renews and there is no minimum term. A service whose whole argument is that hardening decays would be a poor thing to lock anyone into. If a month produces nothing worth reporting, the record says so and that is a fine outcome.
The retainer is not monitoring and it is not alerting. It produces no dashboard, no graph and no page in the middle of the night. Monitoring watches that something you want up is up; this compares what your site is built from against what it was built from last month. If graphs and paging are what you want, that is hosted monitoring and a different service.
There are no figures on this page, and that is deliberate rather than coy. A fixed number would be too high for a brochure site with six plugins and too low for a store with forty and a checkout that must not break. Tell me the shape of it in the form — what the site does, roughly how many plugins, and whether it takes money — and you get a figure back before anything is committed to.
What this cannot promise
Stated here rather than discovered later.
Five things I will not claim
- That a scan can tell you whether you have already been broken into. It cannot, mine included, and Sucuri says the same about its own. The baseline sees inside the box once, and the comparison sees what changed since. Neither is a breach verdict.
- That hardening lasts. A site hardened in August is one plugin auto-update away from re-exposed in November. That is not a failure of the hardening; it is what WordPress is. It is also the entire reason the last tier exists, and why this page will not sell you the first tier as permanent.
- That hiding things fixes them. My own checker puts it exactly right:
hiding the version
does not patch anything, and it does take you out of that list
. Both halves. Concealment buys you the window between a disclosure and your update; only updating closes it, and anyone selling you obscurity as security is selling you the first half without the second. - That updating everything immediately is safe. It is the advice that takes checkout down. The honest version is a copy, a verification and a way back, which is why an update is work rather than a click.
- That this is incident response. It is not. If something surfaces that looks like a live compromise, you get told plainly on the day and pointed at people who do that work. Dressing a scan up as a breach verdict is the exact dishonesty this page is written against.
What you have to supply
Short list, and the second item changes the shape of everything after it.
Five things
- The site’s address, for the public read. That part needs nothing else from you and you can do it yourself right now with the free checker.
- Whether the site is transactional — a store, a membership, a booking system. Asked before anything is quoted, because it decides whether every update needs a copy and a verification round.
- Read access to the install for the baseline, or an export of the plugin and theme inventory and the user list if you would rather not grant it. Either works; the export is slower and slightly less complete, and I will tell you which parts are missing rather than quietly leaving them out.
- For anything past the baseline: a way to apply changes on a copy first, or a named person who applies them while I verify.
- A list of anything that must not be touched — a custom plugin, a pinned version, an integration that breaks on update. This list is usually shorter than people expect and always worth writing down.
Seven things this is not
Each one is real work and most of it is somebody’s job here. It is just not this page’s.
Sent elsewhere rather than quietly included
- Server hardening — display errors, directory listings, the firewall, SSH, ModSecurity. That is the box rather than the application, and my own module already routes those two findings to server hardening at the point it raises them.
- Incident response and malware removal. If a compromise is found, that is a handoff on the day, not a tier.
- Backups, and proving a restore works. This page asks you to have one and does not vouch for it. Proving it is the resilience review.
- Building the deployment pipeline for the site. That is the deployment review.
- Uptime and domain-expiry monitoring, and Grafana-style observability — the monitoring pages, and a different question entirely.
- Writing your custom plugin or theme. Checking whether an update breaks it, and mitigating until it is fixed, is in scope. Fixing it upstream is not.
- Migration, or a hosting change. A different conversation, and worth having separately.
Run the free checker first. Then tell me what your site is actually built from.
It costs nothing, needs no signup and no email address, and it prints the finding id beside the evidence for it. If it comes back clean, take that and go — you have saved a fee and I would rather tell you that than take it. The thing worth talking about is not what it found today. It is that nothing, mine included, can tell you what your site looked like last month.
Prefer to talk? Book a free call ↗ · Or hire me on Upwork ↗ · Typical reply within one business day.