Home  /  Blog  /  xmlrpc.php

What is xmlrpc.php and why you should disable it (or rate-limit it)

It is the most-attacked file on the average WordPress site — and most of the people who own those sites have never opened it. Here is what xmlrpc.php actually does, why it shows up at the top of nearly every Lockora audit report, and how to shut it down without breaking the things that still depend on it.

What xmlrpc.php is, in 60 seconds

xmlrpc.php is a single PHP file that ships in the root of every WordPress install. It exposes the XML-RPC API — a remote interface that lets external clients post articles, manage comments, upload media, and trigger pingbacks without going through the admin UI.

It was introduced in WordPress 2.6 (2008), back when desktop blogging clients like Windows Live Writer, MarsEdit, and Ecto were how serious writers actually published. It is, by any reasonable measure, a relic. In 2016 WordPress shipped a modern alternative — the REST API at /wp-json/ — that handles every use case XML-RPC was built for, with better authentication and saner rate limits.

And yet xmlrpc.php is still enabled by default. Not because anyone is using it, but because deprecating it would break a long tail of older plugins, mobile apps, and self-hosted publishing tools. The result is that every WordPress site on the public internet ships with a wide-open, deeply scriptable, almost-never-used remote API hanging off its front door.

Why attackers love it

Three reasons. None of them depend on a CVE — they are abuses of features that work exactly as documented.

  1. It bypasses the front-door login throttle. Plugins like Limit Login Attempts and Wordfence watch wp-login.php. They do not always watch xmlrpc.php with the same diligence, even though it accepts the same username and password.
  2. It accepts batched requests. The system.multicall method lets a single HTTP request contain hundreds of method calls. A naive rate limiter that counts requests will not catch an attacker making 500 login attempts in one request.
  3. It can be coerced into making outbound HTTP calls. The pingback.ping method tells WordPress to fetch a URL "to verify a pingback." An attacker controls the URL. Multiply that by tens of thousands of unwitting WordPress sites, and you have a reflection DDoS botnet for free.

Brute-force amplification via system.multicall

Here is the attack in its simplest form. The attacker sends one POST to /xmlrpc.php:

POST /xmlrpc.php HTTP/1.1
Host: example.com
Content-Type: text/xml

<methodCall>
  <methodName>system.multicall</methodName>
  <params><param><value><array><data>
    <value><struct>
      <member><name>methodName</name>
        <value><string>wp.getUsersBlogs</string></value></member>
      <member><name>params</name><value><array><data>
        <value><array><data>
          <value><string>admin</string></value>
          <value><string>password1</string></value>
        </data></array></value>
      </data></array></value></member>
    </struct></value>
    <!-- ...499 more entries with different passwords... -->
  </data></array></value></param></params>
</methodCall>

That is one HTTP request, one log line, one entry in your fail2ban counter — and 500 password guesses. A 50,000-attempt dictionary attack that would take an hour against wp-login.php takes minutes against xmlrpc.php, with most security plugins reporting "1 failed login" because they count requests rather than method calls inside the request body.

WordPress 4.4 added a soft mitigation that disables system.multicall for unauthenticated callers after a few failures, but it is per-IP and trivially defeated by rotating through a proxy pool. We still see successful credential stuffing via this path in audits today. See the WordPress XML-RPC server reference for the full method list.

Pingback DDoS: turning your site into a weapon

This is the abuse that, in the mid-2010s, took down enough sites that Akamai and Sucuri started publishing post-mortems about it. The mechanic is simple:

  1. The attacker picks a target — let us call it victim.com.
  2. They build a list of WordPress sites with xmlrpc.php reachable. There are tens of millions.
  3. For each one, they send a pingback.ping call telling WordPress: "Hi, the URL https://victim.com/?[long random string] linked to a post on your site. Please fetch it to confirm."
  4. Every WordPress site in the list dutifully makes an outbound HTTPS request to victim.com, with WordPress's own User-Agent.

The attacker spent one small request per reflector and got a large request to the victim in return — from thousands of legitimate, geographically distributed WordPress sites. The victim cannot block "WordPress" without blocking the open web, and the reflectors did nothing technically wrong. This is why your hosting provider sometimes emails you to say your site was "participating in an attack" you never authorized.

Showing up in your audit?

If Lockora flagged xmlrpc.php exposed without rate limiting, the underlying check is reachability + lack of a rate-limit layer (Wordfence Live Traffic, Cloudflare WAF rule, or a hard block at the web-server level). The fix below resolves the finding.

Do you still need xmlrpc.php at all?

Honest answer: probably not. But there are real exceptions. Disable carefully if any of these apply to you:

If none of the above describe your site — and for the average WordPress site they do not — disable xmlrpc.php outright. If one or more apply, rate-limit it instead.

How to disable xmlrpc.php safely

Option 1: Block at the web server (recommended)

The cleanest fix is to never let the request reach PHP at all. For Apache, drop this into your .htaccess at the WordPress root:

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

For Nginx, in the server block:

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

Disabling access_log is a small but meaningful improvement: a hardened site will receive thousands of xmlrpc.php probes per day, and you do not need to log them all.

Option 2: Disable via WordPress filter

If you do not have edit access to the web-server config, drop this into a mu-plugin (/wp-content/mu-plugins/disable-xmlrpc.php):

<?php
add_filter( 'xmlrpc_enabled', '__return_false' );
add_filter( 'wp_headers', function( $headers ) {
    unset( $headers['X-Pingback'] );
    return $headers;
} );
add_action( 'xmlrpc_call', function() {
    status_header( 403 );
    exit;
} );

This is less efficient than a web-server block (WordPress still has to boot) but it works on managed hosting where you do not control the front-end server.

Option 3: A plugin

If you prefer a plugin, "Disable XML-RPC-API" and "Disable XML-RPC Pingback" both work. We mention plugins last on purpose: every plugin is a new piece of code you have to keep up to date. For something this simple, three lines in .htaccess is the lower-maintenance choice.

How to rate-limit it (if you cannot disable it)

If you need XML-RPC for Jetpack or a mobile app, the goal becomes: let real clients through, throttle everyone else hard.

At Cloudflare

Cloudflare's free plan supports rate-limiting rules. Create a rule that matches:

If you use Jetpack, add an allowlist rule for the WordPress.com IP ranges (Automattic publishes them) above the block rule so Jetpack traffic flows freely.

At Nginx

Use the limit_req module:

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

location = /xmlrpc.php {
    limit_req zone=xmlrpc burst=3 nodelay;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

At the WordPress layer

The xmlrpc_methods filter lets you remove the worst-abused methods while leaving the rest functional. This is the right move if you need the mobile app but not pingbacks:

add_filter( 'xmlrpc_methods', function( $methods ) {
    unset( $methods['pingback.ping'] );
    unset( $methods['pingback.extensions.getPingbacks'] );
    return $methods;
} );

You can also disable system.multicall the same way to remove the brute-force amplifier.

How to verify the fix

From any terminal:

curl -sI https://yoursite.com/xmlrpc.php

A disabled endpoint should return HTTP/1.1 403 Forbidden. A working endpoint returns HTTP/1.1 200 OK with a body that says "XML-RPC server accepts POST requests only." A WordPress-filter-disabled endpoint returns 200 to GET and 403 to POST — not ideal, but acceptable.

Also check that <link rel="pingback" href="..."> is gone from your HTML head. Lockora's audit looks at both signals.

Common questions

Will disabling xmlrpc.php affect my SEO?

No. Search engines do not use XML-RPC. They use the REST API and your sitemap.

Will it break trackbacks and pingbacks?

Yes — specifically, the kind initiated by another WordPress site notifying yours that it has linked to you. Pingback comments have been overwhelmingly spam for at least a decade. Most sites already disable them in Settings → Discussion. If you do too, you will not miss them.

What about the WordPress mobile app on a self-hosted site?

Use Users → Profile → Application Passwords to generate a credential that authenticates over the REST API. The mobile app accepts these. Once configured, you do not need XML-RPC enabled at all.

I disabled it but I am still seeing requests in my logs.

That is normal — the scanners do not know your site refused them, they just keep trying. As long as you are returning 403 with no PHP execution behind it, the impact is roughly zero.

Run a full audit, not just the xmlrpc check.

Lockora Audit checks for this finding and a few hundred more — in under three minutes.

Install the plugin