Skip to main content
Blog|
Learning center

WordPress XML-RPC: what it is and how to disable or secure it

|
Apr 17, 2026|10 min read
LEARNING CENTERWordPress XML-RPC: what it isand how to disable or secureitHOSTNEYhostney.comApril 17, 2026

XML-RPC is a remote publishing API that has shipped with WordPress since the very early versions. It was how people published posts to WordPress before REST existed, how third-party editors like Windows Live Writer talked to the site, and how the WordPress mobile apps used to send content. Almost nothing in the modern WordPress ecosystem needs it any more – REST has replaced it for nearly every use case – but it is still enabled by default on every WordPress install, and attackers have spent the last decade perfecting ways to abuse it.

This article explains what XML-RPC actually does, why it is the single most attacked endpoint on most WordPress sites, how to check whether yours is exposed, and three practical ways to lock it down without breaking the handful of legitimate integrations that still use it.

What XML-RPC is#

XML-RPC is a protocol for calling remote procedures over HTTP using XML as the request and response format. WordPress ships with a file at the root called xmlrpc.php , and any client that POSTs a properly-formatted XML body to that URL can invoke functions inside WordPress – publishing posts, fetching the user list, managing comments, running pingbacks.

It predates REST by a decade. When it was introduced, calling functions on a remote server from a desktop application or a mobile client meant XML-RPC or SOAP. Once REST became standard and WordPress shipped its own REST API in 2016, XML-RPC became legacy – retained for backward compatibility with older clients, but no longer the primary way anything talks to WordPress.

The fact that it is legacy is exactly the problem. A feature that nothing on your site uses, but which is exposed to the public internet by default and accepts authenticated operations, is the perfect attack surface.

Why attackers target XML-RPC#

Two specific endpoints make XML-RPC a preferred target for automated attacks: wp.getUsersBlogs and system.multicall .

wp.getUsersBlogs is a credential oracle. It accepts a username and a password, and if the credentials are valid it returns the list of blogs that user has access to. If they are not valid, it returns an auth error. That is a yes/no answer on credential validity – exactly what a brute-force scanner needs. The mechanics of credential stuffing and dictionary attacks are the same ones covered in brute force attacks on WordPress, just directed at a different endpoint. Unlike /wp-login.php , which serves a full HTML page, the XML-RPC response is a tiny XML payload with almost no overhead.

system.multicall is an amplifier. It lets a client batch multiple XML-RPC calls into a single HTTP request. Attackers nest wp.getUsersBlogs calls inside system.multicall and test dozens – sometimes hundreds – of username-password combinations in a single POST. From your server’s perspective, it looks like one request. From the attacker’s perspective, they just tested a chunk of their credential list in one round trip.

The practical consequences:

  • Rate limits configured against /wp-login.php do not catch the attack, because it is not hitting /wp-login.php .
  • Login attempt counters in plugins like Wordfence or Limit Login Attempts often count the outer request as one attempt rather than the inner multicall count, so the plugin under-reports and the threshold is never tripped.
  • One attacker can push through more credential tests per minute against xmlrpc.php than against the login page, with fewer log entries to draw attention.
  • The requests are cheap to make and expensive to serve. Each multicall spawns a PHP-FPM worker that authenticates N times. Under sustained attack, worker pools saturate and real visitors get 503s.

Beyond credential stuffing, XML-RPC also handles pingbacks, which have their own abuse pattern: attackers use a large number of WordPress sites with XML-RPC enabled as reflectors in a DDoS, tricking each site into sending an HTTP request to the target. Your site is not the victim in that scenario – you are the unwitting weapon. It is a reputational problem regardless.

How to check if XML-RPC is enabled on your site#

The simplest check is a GET request:

curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/xmlrpc.php

If the response is 405 , XML-RPC is enabled and rejecting your GET because it only accepts POST. That is the default WordPress state. If the response is 403 or 404 , something is blocking it – either you or your host has already locked it down.

A more useful check is whether it actually processes a request:

curl -s https://yourdomain.com/xmlrpc.php -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'

If XML-RPC is live, you get back an XML response listing every method WordPress exposes. If it is disabled, you get an HTML error page, a 403, or nothing at all. This second check is the one that matters – some setups return 405 to GETs but still accept POSTs, which is the worst of both worlds.

While you are looking: check your server logs for requests to /xmlrpc.php . Almost every WordPress site on the public internet gets probed for it within hours of being indexed. If you see thousands of POSTs from IPs you do not recognise, the attack is already active. That is not a sign of being targeted specifically – it is the default background noise of running a WordPress site.

The three options for locking XML-RPC down#

There is no right answer. The right choice depends on whether anything on your site actually uses XML-RPC. Work through this decision tree:

Do you use Jetpack? Jetpack still uses XML-RPC for some features, and disabling it entirely can break stats, backups, publicize, and a few other Jetpack-managed functions. Jetpack moved most of its traffic to REST a while ago, but XML-RPC is not fully gone.

Do you use the WordPress mobile apps for publishing? The official WordPress mobile apps have been using REST since 2018, so this is rarely a blocker now. If someone on your team is still on a very old version of the app, they would have to update.

Do you have legacy integrations? Older desktop blog editors (Windows Live Writer, MarsEdit on older versions), some self-hosted syndication tools, older pingback-based link-back systems. These are the holdouts. If nothing on your site falls into this category, disable XML-RPC completely. Otherwise, restrict it to known IPs or keep it enabled behind a rate limiter.

Option 1: Disable it completely

The cleanest option. Drop XML-RPC requests at the web server before WordPress ever runs.

Nginx:

location = /xmlrpc.php {
    deny all;
    return 403;
}

Apache (in .htaccess or vhost):

<Files xmlrpc.php>
    Require all denied
</Files>

A plugin like Disable XML-RPC does the same thing from inside WordPress, but it is the wrong layer. The plugin can only reject the request after PHP-FPM has already loaded WordPress, opened a database connection, and bootstrapped the plugin list. Under a sustained XML-RPC brute force, that overhead is the actual problem. A webserver-level deny rejects the request in microseconds without ever touching PHP.

If you have a control panel that lets you configure per-site rules, use that. Otherwise, your host is the right person to ask.

Option 2: Restrict to specific IPs

If you know exactly which IPs use XML-RPC (Jetpack’s published IP ranges, your office static IP, a specific integration server), allow those and deny everything else.

Nginx:

location = /xmlrpc.php {
    allow 192.0.2.10;            # your office IP
    allow 192.0.2.0/24;          # Jetpack's published range (use the current list)
    deny all;
    fastcgi_pass unix:/var/run/php/php-fpm.sock;
    # ... rest of PHP handler
}

Jetpack publishes its current IP ranges in their documentation. They update periodically, so any allowlist you set needs maintenance. This is the most restrictive option that still allows the legitimate integrations to work, but the upkeep costs are real – if Jetpack moves an IP and you do not notice, stats stop working and it takes a while to connect the two events.

Option 3: Leave it enabled but rate-limit and monitor

If you genuinely need XML-RPC open to the internet for some reason, the minimum bar is aggressive rate limiting and an IP-banning system that watches for abuse.

Nginx rate limit on XML-RPC specifically:

limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=3r/m;

location = /xmlrpc.php {
    limit_req zone=xmlrpc burst=5 nodelay;
    fastcgi_pass unix:/var/run/php/php-fpm.sock;
    # ... rest of PHP handler
}

Three requests per minute per IP with a burst of five. Real Jetpack traffic fits inside this window. Attackers pushing hundreds of multicalls per minute do not.

fail2ban rule for XML-RPC abuse:

Attackers hitting XML-RPC for credential brute force generate a distinctive log pattern: repeated POSTs to /xmlrpc.php , usually from the same IP or a small set of IPs, often with a consistent user-agent. A fail2ban filter like this catches them:

[Definition]
failregex = ^<HOST> .* "POST /xmlrpc.php.*" (200|401|403)
ignoreregex =

Paired with a jail that bans after 20 matches in 10 minutes, this stops brute force against XML-RPC the same way it stops brute force against the login page. fail2ban explains the broader mechanics if you have not deployed it before.

The combination – rate limit plus fail2ban – is the realistic “leave it enabled” setup. Each layer handles what the other misses: rate limiting clamps sustained low-level probing, fail2ban bans the IPs that consistently try to evade the rate limit.

What the disable-via-plugin approach gets wrong#

Every WordPress security plugin offers an “disable XML-RPC” toggle. They all do the same thing: register a filter that makes xmlrpc.php return a disabled response. The request still hits PHP, still spins up a WordPress bootstrap, still loads every active plugin, and only then does the security plugin decide to return a “disabled” response.

That is fine for closing the functional hole (nobody can publish via XML-RPC, nobody can brute force credentials through it) but it does nothing for the performance side of the attack. An attacker sending 1,000 multicalls per minute still consumes 1,000 PHP worker executions per minute, which still exhausts your worker pool and still causes 503s for real visitors.

For this reason, every article on this topic – including this one – recommends blocking XML-RPC at the web server rather than inside WordPress. The plugin approach is fine only if you have no control over the webserver and your host will not configure it for you. If that is the case, get a better host.

Common mistakes#

  • Disabling via plugin and calling it done. Closes the functional attack, ignores the performance attack.
  • Forgetting Jetpack uses IPs that change. If you allowlist Jetpack’s ranges and do not update them, features quietly stop working. Either keep the list maintained or do not allowlist at all.
  • Blocking XML-RPC but leaving the REST user endpoint open. /wp-json/wp/v2/users will enumerate your real usernames, which feeds directly into whatever brute force vector the attacker tries next – login page, XML-RPC, SSH. While you are thinking about the login surface, consider whether the WordPress login URL itself is also worth relocating or restricting.
  • Assuming “nobody will find it.” Every WordPress site on the public internet gets probed for XML-RPC within hours of being reachable. There is no obscurity layer here.
  • Checking once and not monitoring. A host might run an update that resets your nginx config. A plugin might change the landscape. Periodically re-run the curl check above to confirm XML-RPC is still blocked where you expect it to be.

How Hostney handles XML-RPC attacks#

Hostney’s edge layer runs a behavioural bot-protection system that sits in front of nginx on every site. It inspects requests before they reach WordPress and scores them against rate, path, and header patterns – and /xmlrpc.php is one of the paths it treats as sensitive by default. A request pattern that looks like XML-RPC multicall brute forcing – high rate, consistent IP, POSTs to xmlrpc.php with suspicious body size distribution – triggers a proof-of-work challenge or a hard block at the edge without ever reaching PHP. This is independent of what individual site owners have configured: the infrastructure handles the credential-oracle attack whether you remember to lock XML-RPC down or not. The same system catches related abuse patterns like the bot traffic that finds the WordPress login page within minutes of a site going live.

Per-account fail2ban jails watch authentication endpoints, ModSecurity rules block known XML-RPC exploit signatures at the webserver layer, and SELinux confines each site’s PHP process so a compromise from any vector cannot spread across accounts. The stack is a defence-in-depth stack, not a single choke point. For the broader view of how this fits together, see is WordPress secure and how to harden it.

Summary#

XML-RPC is a legacy feature that most WordPress sites do not need and that almost every WordPress site has exposed to the public internet. Attackers use it as a credential oracle and as an amplifier for brute force attacks, and a plugin-level “disable” does not stop the performance side of the attack. If nothing on your site uses XML-RPC, block it at the webserver. If Jetpack or a legacy integration needs it, restrict by IP. If you must leave it open, rate-limit aggressively and deploy fail2ban. Either way, do not rely on “nobody will find it” – every scanner on the internet already has.

Related articles