Short answer
The WordPress plugins screen tells you a plugin is enabled. It never tells you
the plugin is working. Those are different claims, and I found two plugins on
this site sitting in the gap between them: one that had been network-active for
months without ever connecting to the service it exists to talk to, and one whose
entire feature was switched off. Both looked completely healthy from the admin.
The way to tell is to go outside the site and look for the effect the plugin is
supposed to produce.
The first one: active, loaded, connected to nothing
I was auditing what this 2GB server was actually spending itself on when I looked
properly at Jetpack. Network-active. Enabled across the install. Reporting no
errors.
It had never been connected to WordPress.com.
That connection is not optional decoration — it is the mechanism. Jetpack is a
client for a hosted service, and without the link almost everything it offers is
inert. The status underneath said as much: not connected, and no modules active.
It had been loading on every request, registering its hooks, and delivering
nothing.
That distinction matters more than it sounds. An installed but deactivated
plugin is dead weight on disk — mildly untidy, essentially harmless. A
network-active but idle plugin is different: it is code that runs. Every
page load pays for it. Every one of its files is reachable. You are carrying the
full cost of the plugin and receiving none of the benefit, which is the worst
square on the board.
Deactivating it was the obvious move. What I did not expect was what turning it
off unlocked.
Removing it let me close XML-RPC entirely
XML-RPC is WordPress’s older remote interface, a single endpoint at
/xmlrpc.php that exposes a long list of callable methods. On a stock install
that list is around eighty. It is how the classic mobile apps talk to a site, how
some connectors publish — and how a great many brute-force and pingback
amplification attempts arrive.
Jetpack was the only thing on this site that used it. Everything else I run
publishes through the REST API instead, which is a different endpoint with
different authentication.
So once Jetpack was off, the question stopped being “how do I harden XML-RPC” and
became “why is this open at all”. I closed it.
Here is what the endpoint answers with now:
curl -s https://example.com/xmlrpc.php \
-H 'Content-Type: text/xml' \
--data '<?xml version="1.0"?><methodCall>
<methodName>system.listMethods</methodName></methodCall>'
system.multicall
system.listMethods
system.getCapabilities
Three, down from roughly eighty. pingback.ping — the method that let anyone
turn a WordPress site into a traffic amplifier pointed at someone else — is
simply not there any more. The X-Pingback header is gone from responses, and
the advertisement for the endpoint is gone from <head>.
The three that will not leave
You cannot remove those last three, and it is worth understanding why so you do
not waste an evening trying.
WordPress registers the system.* methods inside IXR_Server, at a point below
where the xmlrpc_methods filter can reach them. Filter all you like; they
persist.
It also does not matter. system.multicall exists to bundle several calls into
one request and forward them along — and forwarding is all it does. With every
method it could forward to now absent, it is a hallway with no doors. The
version of this attack that used to work, bundling several login attempts into a
single request to slip past per-request rate limiting, has nothing left to bundle.
I know that because I checked rather than assumed: five calls in one multicall,
five identical “that method does not exist” replies. A settings page saying
“XML-RPC disabled” would have told me the same thing with far less authority.
The second one, found four days later
Here is where a one-off becomes a pattern.
I had just finished a core update and went to clear the page cache. The cache
screen said: zero cached pages, zero kilobytes. Both tiers, nothing.
That number is only reassuring if you have just cleared it. I had not. The site
had been serving traffic, and I had personally hit it dozens of times that
evening running checks. A working cache would have had something in it.
Two requests to the same URL, from outside, told the rest:
curl -s https://example.com/ | grep -o ''
Nothing, twice. A working page cache leaves a marker in the HTML it serves. There
was no marker, because there was no cached page, because caching was switched
off — while the plugin sat in the list, network-active, looking exactly as
healthy as every other row.
Every visitor request had been running PHP to build a page from scratch. On a
server in Seoul, for an audience that is mostly not, on 2GB of RAM.
One toggle fixed it. The fix is not the interesting part. The interesting part is
that two entirely different plugins had failed in the same shape, and that shape
is invisible from the only screen most people look at.
What the plugins screen actually tells you
It tells you a plugin is installed and enabled. That is a fact about
configuration. Whether the plugin does its job is a fact about behaviour, and
nothing in the admin will confirm it for you.
The gap has room for a lot:
- a client plugin that never connected to its service
- a feature plugin with its feature turned off
- a plugin whose output another plugin is silently swallowing —
which is how I convinced myself a working security plugin was broken - a plugin that needs a PHP extension the server does not have, which is
how every image on this site quietly stopped being resized
Four different causes. One symptom, which is no symptom at all.
How to check, per plugin, in about a minute
The method generalises. For each plugin, name the observable effect it should
produce, then go look for that effect from outside the site while logged out.
| Plugin type | Observable effect | How to check |
|---|---|---|
| Page cache | A marker or header on served HTML | Request the same URL twice, look for the marker |
| SEO | Title, description, canonical in the source | curl the page, grep the <head> |
| Security / rate limiting | An actual block when you cross the line | Trigger it deliberately, when it is safe to |
| Hosted-service client | A connection status, not an install status | Check the plugin’s own status output, not the plugins list |
| Image or media | Derived files that exist on disk | Ask the REST API what it generated |
The common thread: look for the effect, not the setting. A setting is a
statement of intent. Every failure in this post produced a perfectly correct
settings screen.
The distinction travels. Anything that reports on its own work has a settings
screen of some kind, including an AI agent describing an edit it just made —
which is where I have ended up applying this hardest.
FAQ
How can I tell if a WordPress plugin is actually doing anything?
Do not check the plugins screen. It only tells you a plugin is enabled, which is
a statement about configuration rather than behaviour. Instead identify the
observable effect the plugin is supposed to produce — a header, a comment in the
HTML, a blocked request — and look for that effect from outside the site, logged
out.
Should I disable XML-RPC in WordPress?
Disable it if nothing you run needs it. Its main modern consumers are the classic
mobile apps and some connectors, and if you are publishing through the REST API
you are not using it. Blocking it removes a large surface, including the pingback
method that has been used for years to bounce traffic at other sites.
Why does system.multicall still appear after I disable XML-RPC?
WordPress registers the three system.* methods inside IXR_Server, below the
point where the xmlrpc_methods filter can remove them, so they survive. It does
not matter much: multicall only forwards calls to other methods, and once those
are gone there is nothing left for it to reach.
Wrapping up
Two plugins, four days apart, failing the same way: enabled, running, achieving
nothing. Neither raised an error. Neither looked wrong. One of them was costing
me page speed on every single request and the other was costing me memory and
surface area on a server that does not have much of either.
The admin screen answers “is it on?” and I had been reading that as an answer to
“is it working?”. Those questions have different answers more often than is
comfortable, and only one of them is the question I actually had.
Go and pick your most important plugin — the one you would notice if it stopped.
Now prove it is running, from outside, without opening wp-admin. If you cannot
think of how, that is the finding.
This was the fifth thing on this site to report success while doing nothing of
the sort. All five, and
the single question that would have caught every one,
are in their own post.
Primary sources:
xmlrpc_methods
· xmlrpc_enabled
· WordPress REST API
· WP Super Cache