The snippet every guide hands you — add_filter( 'xmlrpc_enabled', '__return_false' ); — does not disable XML-RPC in WordPress. It switches off the methods that require authentication and nothing else: xmlrpc.php keeps answering requests, pingbacks keep working, and your server keeps booting the whole of WordPress for every attack request that arrives. WordPress’s own hook documentation says this in as many words, and it has said so since 2016.
That matters because the two things people are actually trying to stop — credential stuffing through system.multicall and reflected pingback traffic — are not what that filter touches. This page covers what the file still does on a WordPress 7.1 install, which of the four common approaches stops what, how to confirm the result from the command line instead of guessing, and the cases where blocking it will break your site in a way you will not notice for a week.
The short answer, before the detail
xmlrpc_methods instead. If nothing on your site uses it, block it at the web server or at your CDN, where the request dies before PHP starts. The famous PHP one-liner is the weakest of the three options and does nothing at all for server load, because the request still boots WordPress to be refused.What xmlrpc.php still does in WordPress 7.1
It is the remote-control API WordPress had before the REST API existed, and it is still shipped and still enabled by default. Nothing in the WordPress 7.1 cycle deprecated or removed it — the published roadmap for 7.1 does not mention XML-RPC at all. So if you are waiting for core to turn it off for you, that is not on the schedule.
Four things on a typical site still depend on it, and you need to know which apply to you before you touch anything:
The two attacks behind every guide on this
Brute force amplification through system.multicall
XML-RPC supports batching: one system.multicall request carries many inner calls, and authentication is checked per inner call. So an attacker sends a single HTTP POST containing hundreds of wp.getCategories calls, each with a different username and password. Sucuri’s research on this, which is where the technique entered general awareness, describes guessing “hundreds of passwords within just one HTTP request” and reaching “thousands of passwords” in only three or four requests. A wrong guess comes back as a fault with code 403 and the message “Incorrect username or password”; a right one returns your blog details.
The reason this bothers people is not the raw rate. It is that none of your login hardening sees it. Login attempt limiters, CAPTCHA and two-factor prompts are nearly always bolted onto wp-login.php, and this traffic never goes near wp-login.php. Your dashboard reports a quiet week while twenty thousand credentials get tested.
Reflected pingback traffic
pingback.ping takes a URL and asks your server to go and fetch it. That is the feature working as designed, and it is also a request anyone can make without authenticating. Point a few thousand WordPress sites at one victim and each of them dutifully makes the outbound request — your site becomes one node in someone else’s traffic flood, and your hosting bill reflects it.
There is a second consequence worth knowing if you sit behind a CDN: the fetch leaves from your origin, not from the CDN edge, so a pingback is a cheap way for someone to learn your real server address.
Why the xmlrpc_enabled filter is not an off switch
Read the hook’s documentation and the scope is stated plainly. The filter description begins “Filters whether XML-RPC methods requiring authentication are enabled”, and then adds that “contrary to the way it’s named, this filter does not control whether XML-RPC is fully enabled” and “does not control whether pingbacks or other custom endpoints” that need no authentication are enabled.
This confused people enough to generate a core ticket. Trac #36055 is titled “Filter xmlrpc_enabled only partly works” and is closed as fixed — but the fix was a documentation change in changeset 37025. The behaviour never changed, and a proposed patch to make the filter block pingbacks too was not adopted.
What the one-liner leaves running
xmlrpc_enabled returning false, xmlrpc.php still exists, still returns a valid XML-RPC response, and still handles unauthenticated methods including the pingback family. Crucially, every one of those attack requests still loads WordPress, your plugins and your theme’s functions file before being told no. Your PHP workers do the same amount of work whether the credentials are checked or not.Four ways to disable XML-RPC in WordPress, compared
I judge these on four things, because those are the four that change the outcome: does the request die before PHP boots, does Jetpack survive, can you keep an exception for one IP, and can you verify it from outside the server. Nothing here is rated on how easy it is to paste.
| Approach | Stops request before PHP? | Jetpack keeps working? | Scope to an allowed IP? | Where it falls short |
|---|---|---|---|---|
xmlrpc_enabled filter | No — WordPress fully loads, then refuses | No. This is the method that breaks it | Only with your own conditional code | Leaves pingback and other unauthenticated methods answering. Zero reduction in load. The most recommended and least effective option |
xmlrpc_methods filter | No | Yes — the connection method stays intact | Not applicable — it removes capabilities, not callers | Still loads WordPress per request. Removing system.multicall may upset a legitimate client that batches calls |
| Web server rule (nginx or Apache) | Yes — 403 straight from the server | No, unless you add an allow rule | Yes, by IP or network | Needs config access. On Apache, plugins that rewrite .htaccess can drop your block without telling you |
| CDN, WAF or host firewall | Yes — never reaches your origin at all | No, unless the vendor’s rule allows WordPress.com | Yes, usually by rule | Depends on your plan and vendor. Easy to forget it exists when you later debug a connection problem for an hour |
Read down the first column and the shape of the answer appears. If your goal is security hygiene, any row works. If your goal is to stop paying for the traffic, only rows three and four do anything, and that distinction is the whole point of this page.
Remove only the dangerous methods and keep Jetpack
This is the option most sites should take, and the one almost nobody writes about. Rather than refusing the endpoint, you take the two amplification vectors out of the method table and leave the rest alone. Put it in a small must-use plugin at wp-content/mu-plugins/disable-xmlrpc-methods.php rather than in the theme, so a theme switch cannot quietly undo your security work:
<?php
/**
* Plugin Name: Trim XML-RPC methods
* Description: Removes pingback and multicall methods while leaving the endpoint usable.
*/
add_filter( 'xmlrpc_methods', function ( array $methods ) : array {
// Reflected-traffic vectors.
unset( $methods['pingback.ping'] );
unset( $methods['pingback.extensions.getPingbacks'] );
// Credential-stuffing amplifier. Comment this out if a
// legitimate client of yours batches calls.
unset( $methods['system.multicall'] );
return $methods;
} );
Be honest with yourself about that third unset. system.multicall is a standard part of the protocol and some well-behaved clients use it to avoid chatty round trips. Remove it, then exercise whatever legitimately connects to your site — the mobile app, your management dashboard — before you consider the job done. If something breaks, the first two lines alone still remove the reflection problem.
While you are in there, two cosmetic extras often get recommended alongside this:
// Drop the X-Pingback response header.
add_filter( 'wp_headers', function ( array $headers ) : array {
unset( $headers['X-Pingback'] );
return $headers;
} );
// Drop the RSD discovery link from wp_head.
remove_action( 'wp_head', 'rsd_link' );
Call these what they are: tidying, not defence. No scanner finds xmlrpc.php by reading your headers — it requests the path, because the path is the same on every WordPress site on earth. Removing the header changes nothing about who reaches the file.
Block the file at the web server
If you have established that nothing legitimate uses the endpoint, this is the version that saves you money. On nginx, inside your site’s server block:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
Note the =. An exact-match location is cheaper than a regex and cannot be sidestepped by a request to /xmlrpc.php?x=1. Reload with nginx -t && systemctl reload nginx so a typo fails the test rather than your site. Leave access_log off out for the first week if you want to count what you are blocking.
On Apache 2.4, in the virtual host or in .htaccess above the WordPress rewrite block:
<Files "xmlrpc.php">
Require all denied
</Files>
Need the endpoint for one known caller — your own management server, or a client you control? Allow it narrowly instead of leaving the door open to everyone:
<Files "xmlrpc.php">
Require all denied
Require ip 203.0.113.42
</Files>
Apache users: assume .htaccess will be rewritten
# BEGIN WordPress markers whenever permalinks are saved, and caching and security plugins write their own sections in. Put your rule outside every marked block, and re-check the file after any plugin change. If you have access to the virtual host configuration, put it there instead and the problem disappears. A rule that silently vanished is worse than no rule, because you will keep believing you are protected.Check what breaks before you block anything
Five minutes of looking beats a fortnight of mystery. Before you change a thing, find out whether anything real is using the endpoint. If you have shell access, this separates genuine callers from the noise by source address:
# Which addresses POST to xmlrpc.php, busiest first
awk '/POST \/xmlrpc\.php/ {print $1}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -20
# How much of your traffic this one file is
grep -c 'POST /xmlrpc.php' /var/log/nginx/access.log
Read the result rather than skimming it. Thousands of hits spread over hundreds of addresses is attack traffic and nothing else. A few dozen hits from one or two stable addresses is something you own, and you need to know what it is before you break it. On a Jetpack site, a cluster of WordPress.com-owned addresses is exactly what you expect to see.
Make the change on a staging copy first, then walk the list: open the WordPress mobile app and publish a draft, open Jetpack in wp-admin and look for a connection notice, run whatever backup or monitoring service you pay for, and trigger a test post. If something misbehaves and you cannot tell what, enabling WordPress debugging and reading the error log safely will usually name the caller, and the step-by-step plugin conflict process narrows it down when the log is quiet.
Verify it with curl instead of hoping
Every one of the four approaches produces a different, recognisable response, so you can tell from outside the server which one is actually in force. Run these against your own domain:
# 1. Is the file reachable at all? (GET is refused by design)
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/xmlrpc.php
# 2. Does an unauthenticated method still answer?
curl -s -X POST -H 'Content-Type: text/xml' \
--data '<methodCall><methodName>demo.sayHello</methodName></methodCall>' \
https://example.com/xmlrpc.php
# 3. Which methods are still exposed?
curl -s -X POST -H 'Content-Type: text/xml' \
--data '<methodCall><methodName>system.listMethods</methodName></methodCall>' \
https://example.com/xmlrpc.php | tr ',' '\n' | grep -i -e pingback -e multicall
How to read what comes back:
| What you see | What it means |
|---|---|
| Test 1 returns 405, with a body saying POST requests only | The file is live and serving. This is an untouched WordPress install, and it is also what you get with the xmlrpc_enabled one-liner in place |
| Test 1 returns 403 | A server, CDN or firewall rule is blocking it. This is the outcome you want if nothing legitimate needs the endpoint |
| Test 1 returns 404 | The file is gone or rewritten away. Works, but it will come back on the next core update if someone deleted it by hand |
Test 2 returns a Hello! response | Unauthenticated methods are answering, whatever you believe you configured |
Test 3 lists pingback.ping or system.multicall | Your xmlrpc_methods filter is not loading. Check the file is in mu-plugins and has no syntax error |
That re-check is the habit worth keeping. Hardening decays — rules get overwritten, hosts migrate you to new infrastructure, plugins get replaced. A quick pass through WordPress Site Health and the problems it surfaces alongside this curl check gives you a monthly five-minute routine that catches most regressions.
What to use instead: application passwords on the REST API
If the reason you were looking at XML-RPC is that something needs to publish to your site remotely, the supported answer is the REST API with an application password. Core’s application passwords documentation covers the mechanics: generate one per integration from Users → Profile, it is shown only once at creation, and the feature is available by default when requests are served over HTTPS — which matters, because these are Basic Auth credentials and interceptable without it.
The operational advantage is revocation. A compromised application password is one row to delete, affecting one integration, and your admin login is untouched. A compromised account password is a password reset plus every session invalidated plus working out what else used it.
Two filters are worth knowing from that same document: wp_is_application_passwords_available turns the feature off entirely, and wp_is_application_passwords_available_for_user scopes it per user or role — useful if you want integrations to authenticate as a dedicated restricted account and nobody else to be able to mint credentials.
Application passwords do work with XML-RPC where it is enabled, which is the basis of core ticket #62789’s proposal to allow only application passwords for authenticated XML-RPC requests. That would close the credential-stuffing hole in core. It is an enhancement sitting in “Awaiting Review” with no owner and a note that it is a breaking change, so plan as though it will not arrive.
None of this substitutes for being on a current release. The Click2Shell issue patched in WordPress 7.1.1 and the critical CVE fixed in 7.1.2 were both reachable regardless of how your XML-RPC endpoint was configured. Endpoint hardening is the layer you add after updates, not instead of them.
Should you disable XML-RPC in WordPress?
Whatever you pick, write down what you did and where. The commonest version of this problem I run into is not an unprotected site — it is a site with a block at the CDN, a conflicting rule in .htaccess, a security plugin setting nobody remembers enabling, and a developer spending an afternoon working out why the mobile app will not connect.
Frequently asked questions
Does add_filter( ‘xmlrpc_enabled’, ‘__return_false’ ) disable XML-RPC?
Only partly. WordPress’s own documentation states the filter covers methods requiring authentication and explicitly does not control pingbacks or other unauthenticated endpoints. The file still responds, and every request still loads WordPress before being refused, so you get no reduction in server load. Use it if you want, but do not count it as your defence.
Can I just delete xmlrpc.php?
You can, and the next core update will put it back, because it is a core file. Deleting core files also makes checksum verification report your install as modified, which costs you a useful signal for spotting a real compromise. A server-level deny rule achieves the same result and survives updates.
Will blocking XML-RPC break Jetpack?
Yes. Jetpack’s support documentation says WordPress.com reaches your site over XML-RPC and that blocking the file breaks the connection; Jetpack surfaces a notice in wp-admin when it happens. If you want Jetpack and hardening both, remove the pingback and multicall methods with the xmlrpc_methods filter and leave the endpoint reachable.
Is XML-RPC being removed from WordPress core?
Not on any published schedule. It is still shipped and enabled by default, and the 7.1 roadmap does not mention it. The nearest thing to movement is ticket #62789, which would require application passwords for authenticated XML-RPC requests — open, unassigned, awaiting review, and acknowledged as a breaking change. Decide as though nothing is coming.
How do I know whether XML-RPC is enabled on my site right now?
Request the file and read the status code: curl -s -o /dev/null -w "%{http_code}\n" https://yoursite.com/xmlrpc.php. A 405 means it is live and serving. A 403 means something is blocking it. Then POST demo.sayHello as shown above — if that returns a greeting, unauthenticated methods are still answering whatever your settings screen claims.
Do I still need a login limiter if I block XML-RPC?
Yes. Blocking this endpoint removes one route to your credentials; wp-login.php and the REST API remain. The two measures protect different doors, which is why a site with a login limiter and an open XML-RPC endpoint — the common configuration — reports almost no failed logins while being brute forced constantly.
Does disabling pingbacks hurt my SEO?
No. Pingbacks and trackbacks create comment-style notifications between sites; they are not a ranking signal, and the incoming ones are overwhelmingly spam. Removing pingback.ping stops your server being used to flood other people, which is the only thing at stake here.
Sources
- xmlrpc_enabled in the WordPress hook reference — the scope note quoted above
- Trac #36055: Filter xmlrpc_enabled only partly works — closed as fixed by a documentation change in changeset 37025
- Trac #62789: Enforce use of application passwords for authenticated XML-RPC requests — status, milestone and the mobile app note
- Application Passwords in the WordPress advanced administration handbook — HTTPS requirement and both availability filters
- Jetpack and XML-RPC — Jetpack’s own statement on the connection and what blocking the file does
- Brute Force Amplification Attacks Against WordPress XMLRPC, Sucuri — the
system.multicalltechnique and the figures quoted - Roadmap to 7.1 on Make WordPress Core — checked for XML-RPC changes; there are none

Leave a Reply to This Post