Disable XML-RPC in WordPress: What Actually Works

Disable XML-RPC in WordPress: What Actually Works

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

If you run Jetpack or sign in from the WordPress mobile apps, do not block the file — strip the dangerous methods with 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 Jetpack connection. Jetpack’s own support documentation is blunt about this: your site acts as the server WordPress.com talks to over XML-RPC, blocking the file breaks the connection, and Jetpack intends to stay on XML-RPC “for as long as we can”. If a security plugin or host firewall has blocked it, Jetpack shows a notice in wp-admin rather than failing silently.
Pingbacks and trackbacks. The mechanism by which one WordPress site tells another it has linked to it. Almost nobody wants these any more, and they are half the reason the endpoint is attacked.
The WordPress mobile apps. For self-hosted sites these have historically authenticated over XML-RPC. Core ticket #62789, which proposes requiring application passwords for authenticated XML-RPC requests, is still open and awaiting review — and notes the apps did not yet support the application-password flow. Test your own app login on a staging copy before you decide.
Older third-party tooling. Desktop blogging clients, a handful of backup and migration services, and some multi-site management dashboards still speak XML-RPC. Your access log will tell you which, and there is a command for that further down.

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

With 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.

ApproachStops request before PHP?Jetpack keeps working?Scope to an allowed IP?Where it falls short
xmlrpc_enabled filterNo — WordPress fully loads, then refusesNo. This is the method that breaks itOnly with your own conditional codeLeaves pingback and other unauthenticated methods answering. Zero reduction in load. The most recommended and least effective option
xmlrpc_methods filterNoYes — the connection method stays intactNot applicable — it removes capabilities, not callersStill 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 serverNo, unless you add an allow ruleYes, by IP or networkNeeds config access. On Apache, plugins that rewrite .htaccess can drop your block without telling you
CDN, WAF or host firewallYes — never reaches your origin at allNo, unless the vendor’s rule allows WordPress.comYes, usually by ruleDepends on your plan and vendor. Easy to forget it exists when you later debug a connection problem for an hour
Filter behaviour per WordPress’s hook documentation and core ticket #36055; Jetpack behaviour per Jetpack’s own XML-RPC support page.

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

WordPress regenerates the block between its own # 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 seeWhat it means
Test 1 returns 405, with a body saying POST requests onlyThe 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 403A server, CDN or firewall rule is blocking it. This is the outcome you want if nothing legitimate needs the endpoint
Test 1 returns 404The 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! responseUnauthenticated methods are answering, whatever you believe you configured
Test 3 lists pingback.ping or system.multicallYour xmlrpc_methods filter is not loading. Check the file is in mu-plugins and has no syntax error
Run test 1 again a week later. A 405 where you expected a 403 is how you find out a plugin rewrote your .htaccess.

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?

Jetpack installed, mobile app in daily use: do not block the file. Deploy the xmlrpc_methods must-use plugin, keep pingback.ping out of the method table, and leave the connection path alone. You remove both attack classes and break nothing.
No Jetpack, no app, you edit in the browser: block it at the web server or the CDN. This is the large majority of sites and the clearest yes on this page. Verify with test 1 above and move on.
Shared hosting with no config access: the xmlrpc_methods filter plus whatever endpoint blocking your host’s firewall offers. Ask support to block the path at their edge — on most managed WordPress hosts it is already done, and worth confirming before you build your own.
Something unidentified is hitting the endpoint: find out what it is first. Blocking an unknown caller is how a nightly backup stops running in silence for three months. Read the log, name the address, then decide.
Running a store: do it, and do it at the edge, because a checkout competing for PHP workers with XML-RPC brute force is a revenue problem rather than a security one. Pair it with the hardening in WooCommerce security plugins compared, judged on what each one actually blocks.
You only pasted the one-liner two years ago: you are not protected in the way you think. Run test 2. If demo.sayHello answers, your pingback exposure never went away.

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

Leave a Reply to This Post