XML-RPC is a remote procedure call protocol that WordPress has supported since its early days. The file xmlrpc.php sits in your WordPress root directory and provides an API for external applications to communicate with your site. While this was genuinely useful back in 2008 when there were no better alternatives, the REST API (introduced in WordPress 4.7, December 2016) has completely replaced it for legitimate use cases. Today, XML-RPC is primarily exploited by attackers for brute force login attempts and DDoS amplification attacks. If you are not using Jetpack or a legacy mobile publishing tool, you should disable it.
The History of XML-RPC in WordPress
XML-RPC support has been part of WordPress since version 1.5 (2005), but it was disabled by default. Site owners had to manually enable it in Settings if they wanted to use desktop blogging clients like Windows Live Writer or MarsEdit. In WordPress 3.5 (December 2012), the XML-RPC interface was enabled by default and the option to disable it through the admin UI was removed. The reasoning was that mobile apps and external services increasingly relied on it. However, this decision also meant that every WordPress installation now had an open API endpoint that attackers could target.
With WordPress 4.7 (December 2016), the REST API became part of WordPress core. The REST API offers a modern, JSON-based interface that is more flexible, better documented, and easier to secure than XML-RPC. The WordPress mobile app switched from XML-RPC to the REST API in 2019. At this point, the only major service still requiring XML-RPC was Jetpack, and even Jetpack has been reducing its XML-RPC dependency over time.
How the system.multicall Brute Force Attack Works
The most dangerous aspect of XML-RPC is the system.multicall method. Normal brute force attacks against the WordPress login page try one username/password combination per HTTP request. Rate limiting and plugins like Limit Login Attempts can effectively block these attacks because each attempt generates a separate request.
XML-RPC's system.multicall changes the equation completely. An attacker can bundle hundreds or even thousands of wp.getUsersBlogs authentication attempts into a single HTTP request. From the server's perspective, this looks like one request. From the attacker's perspective, they just tested 500 passwords. Login rate limiting plugins that work at the application level often do not catch these attempts because they only see one incoming request.
Here is what a multicall brute force payload looks like:
<?xml version="1.0"?>
<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><string>admin</string></value>
<value><string>password123</string></value>
</data></array></value>
</member>
</struct></value>
<!-- hundreds more attempts follow -->
</data></array></value>
</param>
</params>
</methodCall>DDoS Amplification via Pingbacks
The second major attack vector is the XML-RPC pingback feature. Pingbacks are notifications sent between WordPress sites when one site links to another. Attackers abuse this by sending forged pingback requests to thousands of WordPress sites, all pointing to a single target URL. Each WordPress site then sends a verification request to the target, effectively turning thousands of WordPress installations into a distributed denial of service (DDoS) botnet. The target server gets overwhelmed with incoming requests from legitimate WordPress sites, making it difficult to block.
How to Test If XML-RPC Is Currently Enabled
Before making changes, check whether XML-RPC is active on your site. You can use curl from the command line:
curl -X POST https://yoursite.com/xmlrpc.php -H "Content-Type: text/xml" -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'If XML-RPC is enabled, you will receive an XML response listing all available methods (typically 80+ methods). If it is disabled or blocked, you will get a 403 Forbidden error, a connection refused error, or a "XML-RPC server accepts POST requests only" message (depending on how it was disabled).
Checking Server Logs for XML-RPC Attacks
Before disabling XML-RPC, it is worth checking your access logs to see if you are already being targeted. Look for POST requests to xmlrpc.php:
# Apache access log
grep "xmlrpc.php" /var/log/apache2/access.log | tail -50
# Nginx access log
grep "xmlrpc.php" /var/log/nginx/access.log | tail -50
# Count requests per IP
grep "xmlrpc.php" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20If you see hundreds or thousands of POST requests to xmlrpc.php from various IP addresses, your site is being actively targeted. This is extremely common, and it is one more reason to disable the endpoint entirely.
Method 1: Block at the Web Server Level (Recommended)
Blocking XML-RPC at the web server level is the most effective approach because the request is rejected before PHP even loads. This saves server resources and is the approach you should prefer over PHP-level filtering.
Apache (.htaccess)
Add this to your .htaccess file in the WordPress root directory:
# Block all access to xmlrpc.php
<Files xmlrpc.php>
Order deny,allow
Deny from all
</Files>If you need to allow specific IP addresses (for example, if you use Jetpack), you can add exceptions:
<Files xmlrpc.php>
Order deny,allow
Deny from all
Allow from 122.248.245.244
Allow from 54.217.201.243
</Files>Nginx
Add this inside your server block in the Nginx configuration:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
return 403;
}The access_log off and log_not_found off directives prevent your log files from filling up with blocked XML-RPC attempts, which can grow to gigabytes on heavily targeted sites.
Method 2: Disable via WordPress Filter (PHP Level)
If you cannot modify server configuration files (common on shared hosting), you can disable XML-RPC at the PHP level. Add this to your theme's functions.php or, better yet, to a custom must-use plugin:
// Disable XML-RPC entirely
add_filter('xmlrpc_enabled', '__return_false');
// Remove the XML-RPC discovery link from the HTML head
remove_action('wp_head', 'rsd_link');
// Remove the X-Pingback HTTP header
add_filter('wp_headers', function($headers) {
unset($headers['X-Pingback']);
return $headers;
});Important: The xmlrpc_enabled filter only disables authentication-based XML-RPC methods. The xmlrpc.php file still loads, PHP still executes, and unauthenticated methods (like pingbacks) may still work. That is why blocking at the server level is preferred. The filter approach still consumes server resources processing the request before ultimately rejecting it.
Creating a Must-Use Plugin (Recommended over functions.php)
Instead of adding code to functions.php (which gets overwritten during theme updates), create a must-use plugin. Create the file wp-content/mu-plugins/disable-xmlrpc.php:
<?php
/**
* Plugin Name: Disable XML-RPC
* Description: Completely disables XML-RPC functionality
*/
add_filter('xmlrpc_enabled', '__return_false');
remove_action('wp_head', 'rsd_link');
add_filter('wp_headers', function($headers) {
unset($headers['X-Pingback']);
return $headers;
});Must-use plugins load before regular plugins and cannot be accidentally deactivated through the admin interface.
Method 3: Security Plugin Configuration
If you use a security plugin like Wordfence, Sucuri, or iThemes Security, they offer built-in options to disable XML-RPC:
- Wordfence: Go to Wordfence > All Options > Brute Force Protection. The firewall automatically rate-limits XML-RPC authentication attempts. For complete blocking, use the server-level method above.
- iThemes Security: Go to Security > Settings > WordPress Tweaks. Toggle "Disable XML-RPC" to completely block it, or select "Disable Pingbacks" to only block the pingback abuse vector while keeping other XML-RPC methods functional.
- Sucuri: The Sucuri Web Application Firewall (cloud-based) can block XML-RPC at the CDN level before requests even reach your server.
Cloudflare WAF Rules for XML-RPC Protection
If you use Cloudflare, you can create a WAF rule that blocks XML-RPC requests at the edge, before they reach your server at all. Go to Security > WAF > Custom Rules and create a rule:
- Rule name: Block XML-RPC
- Expression:
(http.request.uri.path contains "/xmlrpc.php") - Action: Block
This is the most efficient blocking method because Cloudflare handles the request at their edge servers. Your origin server never sees the traffic. If you need to allow Jetpack, add an exception for Automattic's IP ranges.
fail2ban Configuration for XML-RPC Brute Force
If you manage your own server (VPS or dedicated), you can use fail2ban to automatically ban IP addresses that repeatedly hit xmlrpc.php. Create a filter file at /etc/fail2ban/filter.d/wordpress-xmlrpc.conf:
[Definition]
failregex = ^<HOST> .* "POST .*xmlrpc.php.*" (200|403)
ignoreregex =Then add a jail in /etc/fail2ban/jail.local:
[wordpress-xmlrpc]
enabled = true
port = http,https
filter = wordpress-xmlrpc
logpath = /var/log/apache2/access.log
maxretry = 5
findtime = 60
bantime = 3600This configuration bans any IP address that makes more than 5 requests to xmlrpc.php within 60 seconds, blocking them for one hour. Adjust the values based on your traffic patterns.
XML-RPC vs. REST API: Security Comparison
Understanding why the REST API is more secure helps explain why XML-RPC should be retired:
- Authentication: The REST API supports multiple authentication methods (cookie auth, application passwords, OAuth) and has built-in nonce verification. XML-RPC sends credentials in plain text within the XML body on every request.
- Rate limiting: REST API requests can be rate-limited per endpoint. XML-RPC's multicall method makes per-request rate limiting ineffective.
- Permissions: The REST API respects WordPress capability checks and provides granular permission callbacks for each endpoint. XML-RPC has coarser access control.
- Input validation: REST API endpoints use schema-based input validation. XML-RPC relies on the individual method implementations for validation.
- No multicall equivalent: The REST API does not have a batch endpoint that allows bundling hundreds of authentication attempts into a single request.
Check if You Still Need XML-RPC Before Disabling
Before you disable XML-RPC, verify that none of your active services require it:
- Jetpack: Some Jetpack features still communicate via XML-RPC. If you use Jetpack, test your site after disabling XML-RPC. If features break, you may need to allow Automattic's IP addresses or use the server-level allow-list method shown above.
- WordPress mobile app: Current versions of the WordPress mobile app use the REST API. Only very old versions (before 2019) used XML-RPC. If your app works fine, you do not need XML-RPC.
- IFTTT or Zapier integrations: Some legacy integrations with automation services used XML-RPC. Check if your automations still work after disabling.
- Windows Live Writer or other desktop editors: These legacy tools used XML-RPC for publishing. If you still use one, consider switching to the WordPress block editor or a REST API-based alternative.
Verifying That XML-RPC Is Disabled
After applying your chosen method, verify that XML-RPC is actually blocked:
# Should return 403 Forbidden or connection refused
curl -s -o /dev/null -w "%{http_code}" -X POST https://yoursite.com/xmlrpc.php -H "Content-Type: text/xml" -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'If you get a 403 status code, XML-RPC is blocked. If you still get a 200, your blocking method is not working correctly. Check your configuration and make sure the server was restarted (for Nginx) or that .htaccess changes are being read (for Apache, ensure AllowOverride is enabled).
You can also run an InspectWP scan. The security section detects whether XML-RPC is accessible and flags it as a potential risk if it responds to requests.