Table of Contents
- Understanding WordPress XML-RPC Vulnerabilities
- XML-RPC Attack Vectors and Amplification
- Disabling XML-RPC Completely
- Hardening XML-RPC Access
- Selective Method Enabling
- Monitoring and Logging XML-RPC Traffic
- XML-RPC Bypass Detection
- Testing Your XML-RPC Security
Understanding WordPress XML-RPC Vulnerabilities
XML-RPC is a remote procedure call protocol that allows external applications to interact with WordPress. Originally created to enable mobile apps and publishing tools to manage WordPress remotely, XML-RPC has become one of WordPress's most exploited attack vectors. The irony is that XML-RPC was designed to make WordPress easier to use from third-party applications, but it inadvertently created an avenue for attacks.
The core vulnerability isn't a bug in XML-RPC itself—it's an architectural problem. XML-RPC provides an alternative authentication mechanism to the WordPress login page, uses the same credential validation, but with different rate limiting (often none), and is less monitored by security tools. An attacker attacking through XML-RPC appears different from one attacking /wp-login.php, making detection harder. Your WordPress login form might have rate limiting that blocks login attempts after 5 failed tries. But XML-RPC might accept unlimited authentication attempts because it was designed for server-to-server communication where rate limiting seemed unnecessary. An attacker exploiting this difference can make unlimited password guesses against your XML-RPC endpoint while your WordPress login page happily blocks them.
XML-RPC sits at /xmlrpc.php on every WordPress installation and is enabled by default. Even if your theme never uses it, even if no plugins use it, it's running and waiting to be attacked. This default-enabled approach made sense when XML-RPC was new and widely used. Now, in 2026, most applications have migrated to the REST API, but XML-RPC remains enabled for backward compatibility.
The challenge with WordPress XML-RPC security is deciding whether to disable it entirely or harden it. Some users need XML-RPC for legitimate applications—mobile apps, desktop clients, scheduled publishing tools. Complete disabling breaks these use cases. But hardening without disabling requires careful configuration. You need to understand not just what XML-RPC is, but what specific methods your legitimate applications use, so you can allow those methods while blocking dangerous ones.
WP HealthKit's security audit checks whether your XML-RPC endpoint is unnecessarily exposed, whether it's properly rate-limited and monitored, and whether your configuration prevents common attack patterns.
XML-RPC Attack Vectors and Amplification
Attack Vector 1: Brute Force Attacks
XML-RPC uses the same authentication as WordPress but with a different code path. An attacker can use wp.getUsersBlogs() or blogger.getUsersBlogs() to test credentials without hitting the login form's rate limiting:
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value><string>admin</string></value></param>
<param><value><string>guessedpassword</string></value></param>
</params>
</methodCall>
If the credentials are wrong, XML-RPC returns an error. If correct, it returns user information. An attacker can automate this to brute force passwords with minimal detection.
Attack Vector 2: Amplification Attacks (DDoS)
XML-RPC methods can be chained to perform expensive operations. The system.multicall method allows executing multiple methods in a single request. An attacker could execute thousand of expensive operations against WordPress or other systems:
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>system.multicall</methodName>
<params>
<param>
<value>
<array>
<data>
<value>
<struct>
<member>
<name>methodName</name>
<value><string>wp.getPostList</string></value>
</member>
<member>
<name>params</name>
<value>
<array><data></data></array>
</value>
</member>
</struct>
</value>
<!-- Repeat same method 1000s of times -->
</data>
</array>
</value>
</param>
</params>
</methodCall>
This type of attack consumes server resources, causes performance degradation, and can bring sites offline. The beauty of this attack from an attacker's perspective is that it's completely legitimate-looking—they're just calling XML-RPC methods. Your server can't distinguish between an attacker making 10,000 requests and a legitimate application making 10,000 requests if both use valid authentication credentials or valid public methods. The solution requires rate limiting based on the quantity of requests, not just their validity.
Attack Vector 3: Information Disclosure
XML-RPC methods can leak information about your WordPress installation:
wp.getPostList()reveals post titles, dates, and contentwp.getCommentList()exposes commenter informationwp.getAuthors()lists all user accountswp.getOptions()can reveal configuration details
This information helps attackers plan more targeted attacks.
Attack Vector 4: Pingback and Trackback Abuse
Older XML-RPC methods like pingback.ping were used to create links between sites. These methods are vulnerable to abuse:
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://attacker.com/malicious-page</string></value></param>
<param><value><string>http://target-site.com</string></value></param>
</params>
</methodCall>
Attackers can use pingback to create spam comments, trigger notifications, or perform other malicious actions.
Disabling XML-RPC Completely
If you don't need XML-RPC, disabling it entirely is the safest option. This removes the attack surface completely. The principle here is simple: no code running means no vulnerabilities in that code. You can't have an XML-RPC attack if XML-RPC isn't executing. This is the single most effective defense against XML-RPC attacks—better than rate limiting, better than authentication, better than any other hardening approach.
Before disabling XML-RPC, verify that you actually don't need it. Check whether any of your plugins use XML-RPC. Check whether you have any mobile apps or publishing tools that connect via XML-RPC. Ask your team whether anyone uses XML-RPC functionality. If nobody uses it, disabling is the clear choice. If someone does use it, you have a decision to make: disable it and ask them to use alternative tools (like the REST API), or harden it carefully.
Method 1: Disable at WordPress Level
// Add to functions.php or plugin
add_filter( 'xmlrpc_enabled', '__return_false' );
This is the simplest approach but doesn't actually disable XML-RPC—it just hides it from the WordPress admin. The /xmlrpc.php endpoint still responds to requests.
Method 2: Disable in wp-config.php
// Add to wp-config.php before the line defining ABSPATH
define( 'XMLRPC_REQUEST', false );
if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
wp_die( 'XML-RPC is disabled' );
}
Method 3: Proper Plugin-Based Disabling
Create a plugin that completely blocks XML-RPC requests:
<?php
/**
* Plugin Name: WP HealthKit XML-RPC Blocker
* Description: Properly disable XML-RPC endpoint
* Version: 1.0.0
*/
// Block XML-RPC before WordPress loads
if ( basename( $_SERVER['SCRIPT_FILENAME'] ) === 'xmlrpc.php' ) {
die( 'XML-RPC is disabled' );
}
// Alternative: use a mu-plugin for early blocking
// Place this file in wp-content/mu-plugins/xmlrpc-blocker.php
A must-use plugin (mu-plugin) executes before regular plugins, making it a reliable way to block XML-RPC before it processes any requests.
Method 4: Disable at Web Server Level
This is the most effective approach because it blocks XML-RPC before PHP even processes the request.
For Apache (in .htaccess):
<FilesMatch "^xmlrpc\.php$">
Order Deny,Allow
Deny from all
</FilesMatch>
For Nginx (in server config):
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
This is the preferred method because it provides the earliest possible blocking, uses minimal resources, and logs attempts for monitoring. Web server-level blocking happens before your WordPress installation even starts processing the request. Your database doesn't get queried. Your plugins don't execute. The web server simply closes the connection and returns a 403 or 404 response. This is dramatically more efficient than any WordPress-level blocking, especially under attack when you might receive thousands of XML-RPC requests per second.
Hardening XML-RPC Access
If you need XML-RPC for legitimate applications, hardening rather than disabling is necessary. Hardening means adding multiple layers of protection around the XML-RPC endpoint so that even if an attacker can reach it, they face obstacles at every step. They might bypass one protection layer, but they'll run into another. This defense-in-depth approach makes attacks significantly more difficult and less profitable for attackers.
The hardening strategies include rate limiting (limiting request frequency), authentication (requiring valid credentials), method filtering (allowing only safe methods), and logging (tracking what happens). When implemented together, these approaches create a significantly more secure XML-RPC endpoint than the default configuration.
Rate Limiting XML-RPC:
class WP_HealthKit_XMLRPC_Rate_Limiter {
private $attempt_limit = 5; // attempts per window
private $time_window = 900; // 15 minutes
public function can_execute( $method, $ip_address ) {
// Certain methods are more dangerous than others
$dangerous_methods = array(
'system.multicall',
'wp.getUsersBlogs',
'wp.getAuthors',
'blogger.getUsersBlogs'
);
// Apply stricter limits to dangerous methods
if ( in_array( $method, $dangerous_methods ) ) {
$limit = 2; // Only 2 attempts for dangerous methods
} else {
$limit = $this->attempt_limit;
}
$key = 'wp_healthkit_xmlrpc_' . md5( $ip_address );
$attempts = (int) get_transient( $key );
if ( $attempts >= $limit ) {
return false;
}
return true;
}
public function record_attempt( $method, $ip_address, $success = false ) {
$key = 'wp_healthkit_xmlrpc_' . md5( $ip_address );
$attempts = (int) get_transient( $key );
set_transient( $key, $attempts + 1, $this->time_window );
// Log the attempt
$this->log_attempt( $method, $ip_address, $success );
}
private function log_attempt( $method, $ip_address, $success ) {
global $wpdb;
$table = $wpdb->prefix . 'wp_healthkit_xmlrpc_log';
// Create table if it doesn't exist
$this->create_log_table();
$wpdb->insert(
$table,
array(
'method' => $method,
'ip_address' => $ip_address,
'success' => $success ? 1 : 0,
'timestamp' => current_time( 'mysql' )
),
array( '%s', '%s', '%d', '%s' )
);
}
private function create_log_table() {
global $wpdb;
$table = $wpdb->prefix . 'wp_healthkit_xmlrpc_log';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
method VARCHAR(100) NOT NULL,
ip_address VARCHAR(45) NOT NULL,
success TINYINT(1) NOT NULL,
timestamp DATETIME NOT NULL,
PRIMARY KEY (id),
KEY method (method),
KEY ip_address (ip_address),
KEY timestamp (timestamp)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
}
}
// Hook into XML-RPC before execution
add_action( 'xmlrpc_call', function( $method ) {
$limiter = new WP_HealthKit_XMLRPC_Rate_Limiter();
$ip_address = wp_get_client_ip();
if ( ! $limiter->can_execute( $method, $ip_address ) ) {
wp_die( new IXR_Error( 429, 'Too many XML-RPC requests. Please try again later.' ) );
}
}, 0 );
add_action( 'xmlrpc_call_success_wp_getUsersBlogs', function() {
$limiter = new WP_HealthKit_XMLRPC_Rate_Limiter();
$ip_address = wp_get_client_ip();
$limiter->record_attempt( 'wp.getUsersBlogs', $ip_address, true );
} );
Require Authentication for All Methods:
// Ensure all XML-RPC methods require authentication
add_filter( 'xmlrpc_methods', function( $methods ) {
// Remove methods that don't require authentication
unset( $methods['wp.getCommentCount'] );
unset( $methods['wp.getPostList'] );
unset( $methods['metaWeblog.getRecentPosts'] );
unset( $methods['pingback.ping'] );
return $methods;
} );
IP Whitelisting:
function wp_healthkit_xmlrpc_whitelist_check() {
if ( basename( $_SERVER['SCRIPT_FILENAME'] ) !== 'xmlrpc.php' ) {
return;
}
$whitelist = get_option( 'wp_healthkit_xmlrpc_whitelist', array() );
if ( empty( $whitelist ) ) {
return; // No whitelist configured, allow all
}
$ip_address = wp_get_client_ip();
$ip_allowed = false;
foreach ( $whitelist as $entry ) {
if ( $entry['ip'] === $ip_address ) {
$ip_allowed = true;
break;
}
// Check CIDR ranges
if ( strpos( $entry['ip'], '/' ) !== false ) {
if ( $this->ip_in_range( $ip_address, $entry['ip'] ) ) {
$ip_allowed = true;
break;
}
}
}
if ( ! $ip_allowed ) {
wp_die( 'XML-RPC access denied', 403 );
}
}
add_action( 'init', 'wp_healthkit_xmlrpc_whitelist_check', 0 );
Mid-Article CTA
Unsure if your WordPress XML-RPC configuration is secure? WP HealthKit scans your plugin code to detect unprotected XML-RPC methods, verify rate limiting is in place, and identify dangerous method combinations. Upload your plugin to WP HealthKit for a complete security audit including XML-RPC analysis.
Selective Method Enabling
Some applications need specific XML-RPC methods while others should be blocked. This granular approach balances security and functionality. Not all XML-RPC methods are equally dangerous. A method that returns public blog posts is less risky than a method that creates new posts or modifies comments. A method that authenticates users is riskier than one that simply returns information.
By selectively enabling only the methods your legitimate applications actually use, you reduce the attack surface. An attacker can only attack methods you've left enabled. If your mobile app only needs to read posts, you should disable all methods that write, delete, or modify content. If your publishing tool only needs to create and edit posts, disable methods that enumerate users, reveal configuration, or perform administrative operations.
Whitelist Specific Methods:
class WP_HealthKit_XMLRPC_Selector {
// Methods allowed for specific applications
private $allowed_methods = array(
'mobile_app' => array(
'wp.getUsersBlogs',
'wp.getPost',
'wp.getPages',
'metaWeblog.getPost'
),
'external_publishing' => array(
'metaWeblog.newPost',
'metaWeblog.editPost',
'metaWeblog.deletePost',
'wp.uploadFile'
),
'social_media' => array(
'wp.getPostList',
'wp.getCommentList'
)
);
public function execute_method_if_allowed( $method, $application ) {
if ( ! isset( $this->allowed_methods[$application] ) ) {
wp_die( new IXR_Error( 403, 'Application not recognized' ) );
}
if ( ! in_array( $method, $this->allowed_methods[$application] ) ) {
wp_die( new IXR_Error( 403, "Method '$method' is not allowed for this application" ) );
}
return true;
}
}
// Identify the calling application
add_action( 'xmlrpc_call', function( $method ) {
$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ?
sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) : 'Unknown';
$application = 'unknown';
if ( strpos( $user_agent, 'WordPress Mobile' ) !== false ) {
$application = 'mobile_app';
} elseif ( strpos( $user_agent, 'MarsEdit' ) !== false ) {
$application = 'external_publishing';
} elseif ( strpos( $user_agent, 'IFTTT' ) !== false ) {
$application = 'social_media';
}
if ( $application !== 'unknown' ) {
$selector = new WP_HealthKit_XMLRPC_Selector();
$selector->execute_method_if_allowed( $method, $application );
}
}, 0 );
Filter Methods by User Role:
function wp_healthkit_xmlrpc_filter_by_role( $methods ) {
global $current_user;
// Get authenticated user
if ( empty( $current_user ) || ! isset( $current_user->ID ) ) {
// Unauthenticated - only allow read methods
$allowed = array(
'wp.getPostList' => $methods['wp.getPostList'],
'wp.getPost' => $methods['wp.getPost'],
'wp.getPages' => $methods['wp.getPages']
);
return $allowed;
}
// Subscribers can only read
if ( user_can( $current_user, 'read' ) && ! user_can( $current_user, 'edit_posts' ) ) {
unset( $methods['metaWeblog.newPost'] );
unset( $methods['metaWeblog.editPost'] );
unset( $methods['metaWeblog.deletePost'] );
unset( $methods['wp.uploadFile'] );
}
// Only admins can access system methods
if ( ! user_can( $current_user, 'manage_options' ) ) {
unset( $methods['system.multicall'] );
unset( $methods['wp.getUsers'] );
unset( $methods['wp.getOptions'] );
}
return $methods;
}
add_filter( 'xmlrpc_methods', 'wp_healthkit_xmlrpc_filter_by_role' );
Monitoring and Logging XML-RPC Traffic
Comprehensive monitoring reveals attack patterns and helps identify compromised credentials. Logging provides evidence of what happened if attacks occur. It enables you to detect patterns—dozens of failed authentication attempts from a single IP, for instance—that indicate active attack. Logging also helps you understand how legitimate applications use XML-RPC so you can distinguish legitimate traffic from malicious traffic.
When you see logs showing hundreds of failed attempts from a single IP for a method you don't use, you know you're under attack. When you see logs showing sporadic successful authentic calls mixed with occasional failed calls, that's probably legitimate application behavior with normal network errors. This pattern recognition capability is why comprehensive logging is so important—it transforms raw events into actionable intelligence.
Log All XML-RPC Requests:
function wp_healthkit_log_xmlrpc_request() {
if ( basename( $_SERVER['SCRIPT_FILENAME'] ) !== 'xmlrpc.php' ) {
return;
}
global $wpdb;
// Get the method being called
$raw_post = file_get_contents( 'php://input' );
$method = '';
if ( preg_match( '/<methodName>([^<]+)<\/methodName>/', $raw_post, $matches ) ) {
$method = $matches[1];
}
// Create log table
$table = $wpdb->prefix . 'wp_healthkit_xmlrpc_requests';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
method VARCHAR(100) NOT NULL,
ip_address VARCHAR(45) NOT NULL,
user_agent TEXT,
timestamp DATETIME NOT NULL,
PRIMARY KEY (id),
KEY method (method),
KEY ip_address (ip_address),
KEY timestamp (timestamp)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
// Insert log entry
$wpdb->insert(
$table,
array(
'method' => $method,
'ip_address' => wp_get_client_ip(),
'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ?
substr( sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ), 0, 255 ) : '',
'timestamp' => current_time( 'mysql' )
),
array( '%s', '%s', '%s', '%s' )
);
}
add_action( 'init', 'wp_healthkit_log_xmlrpc_request', 0 );
Dashboard Widget for XML-RPC Activity:
function wp_healthkit_add_xmlrpc_activity_widget() {
wp_add_dashboard_widget(
'wp-healthkit-xmlrpc-activity',
'XML-RPC Activity (24h)',
function() {
global $wpdb;
$table = $wpdb->prefix . 'wp_healthkit_xmlrpc_requests';
$time_cutoff = date( 'Y-m-d H:i:s', time() - 86400 );
$results = $wpdb->get_results( $wpdb->prepare(
"SELECT method, COUNT(*) as count
FROM $table
WHERE timestamp > %s
GROUP BY method
ORDER BY count DESC
LIMIT 10",
$time_cutoff
) );
if ( empty( $results ) ) {
echo '<p>No XML-RPC activity in the last 24 hours.</p>';
return;
}
echo '<table style="width: 100%;">';
echo '<tr><th>Method</th><th>Requests</th></tr>';
foreach ( $results as $row ) {
echo '<tr>';
echo '<td>' . esc_html( $row->method ) . '</td>';
echo '<td>' . intval( $row->count ) . '</td>';
echo '</tr>';
}
echo '</table>';
}
);
}
add_action( 'wp_dashboard_setup', 'wp_healthkit_add_xmlrpc_activity_widget' );
XML-RPC Bypass Detection
Some attackers try to bypass XML-RPC blocks using alternative paths or encoded requests. The cat-and-mouse game of security continues even when you think you've blocked something. An attacker might try encoding their XML-RPC request with gzip compression, assuming your code only checks for plaintext XML-RPC. Or they might base64-encode the entire request. Or they might try to call XML-RPC methods through the REST API. Detection of these bypass attempts is crucial because they indicate that someone is specifically targeting your application and trying to circumvent your security measures.
When you detect bypass attempts, that's a warning sign that you need to audit your security posture more carefully. Why was someone trying to bypass your XML-RPC protection? Do you have other vulnerabilities they might exploit? Have your credentials been compromised? Bypass detection should trigger investigation.
Detect Encoded XML-RPC Requests:
function wp_healthkit_detect_xmlrpc_bypasses() {
// Check for gzip-encoded XML-RPC
if ( isset( $_SERVER['CONTENT_ENCODING'] ) &&
strpos( $_SERVER['CONTENT_ENCODING'], 'gzip' ) !== false ) {
$data = gzuncompress( file_get_contents( 'php://input' ) );
if ( strpos( $data, 'methodCall' ) !== false ) {
wp_die( 'Encoded XML-RPC requests are not allowed' );
}
}
// Check for deflate encoding
if ( isset( $_SERVER['CONTENT_ENCODING'] ) &&
strpos( $_SERVER['CONTENT_ENCODING'], 'deflate' ) !== false ) {
$data = gzinflate( file_get_contents( 'php://input' ) );
if ( strpos( $data, 'methodCall' ) !== false ) {
wp_die( 'Encoded XML-RPC requests are not allowed' );
}
}
// Check for base64 encoding
$raw = file_get_contents( 'php://input' );
if ( preg_match( '/^[A-Za-z0-9+\/]+={0,2}$/', trim( $raw ) ) ) {
$decoded = base64_decode( $raw, true );
if ( strpos( $decoded, 'methodCall' ) !== false ) {
wp_die( 'Base64-encoded XML-RPC requests are not allowed' );
}
}
}
add_action( 'init', 'wp_healthkit_detect_xmlrpc_bypasses', 0 );
Testing Your XML-RPC Security
Verify that your XML-RPC hardening actually works. Security implementations are only as good as your verification that they actually prevent attacks. A rate limiter configured but not tested might not actually block requests. A method disabled in code but still callable through the XML-RPC interface doesn't provide protection.
Testing should simulate attacks. Try making requests that should be blocked—unauthenticated method calls, disabled methods, requests from non-whitelisted IPs. Verify they're actually blocked and not just silently accepted. Try rate-limiting protection by sending repeated requests and verify that you get blocked after the configured limit. Test that logging actually records requests. These tests should be part of your ongoing security verification, not just a one-time check.
Test XML-RPC Methods:
# Test wp.getUsersBlogs
curl -X POST http://example.com/xmlrpc.php \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value><string>admin</string></value></param>
<param><value><string>password123</string></value></param>
</params>
</methodCall>'
# Test dangerous system.multicall method
curl -X POST http://example.com/xmlrpc.php \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>system.multicall</methodName>
<params>
<param><value><array><data></data></array></value></param>
</params>
</methodCall>'
Test Rate Limiting:
function test_xmlrpc_rate_limiting() {
for ( $i = 0; $i < 10; $i++ ) {
$response = wp_remote_post( 'http://example.com/xmlrpc.php', array(
'body' => '<?xml version="1.0"?>
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value><string>admin</string></value></param>
<param><value><string>wrongpass</string></value></param>
</params>
</methodCall>'
) );
$body = wp_remote_retrieve_body( $response );
if ( strpos( $body, 'Too many' ) !== false ||
strpos( $body, 'rate' ) !== false ) {
echo "Rate limiting triggered after $i attempts";
return true;
}
}
echo "Rate limiting not triggered after 10 attempts";
return false;
}
Additional Resources
For a comprehensive view of how WP HealthKit approaches plugin analysis, explore our 17 verification layers or browse the plugin directory to see real audit scores. Ready to check your own plugin? Run a free audit now.
Frequently Asked Questions
Is disabling XML-RPC safe?
Yes, unless you actively use XML-RPC features like mobile app publishing. Modern alternatives like the WordPress REST API provide better security and functionality. If you're not certain you use XML-RPC, disabling it is the safest choice.
What's the difference between disabling in WordPress vs the web server?
WordPress-level disabling is easier to manage but still processes the request. Web server level disabling (via .htaccess or Nginx config) prevents the request from reaching PHP, using minimal resources. For maximum security, disable at the web server level.
How do I know which applications use XML-RPC?
Check your mobile apps, publishing tools, and plugins. WordPress mobile apps use XML-RPC, but newer apps prefer the REST API. If you disable XML-RPC, test your workflows to ensure nothing breaks.
Can I block XML-RPC for attackers but allow it for legitimate applications?
Yes, through IP whitelisting, rate limiting, and method filtering. However, if legitimate applications can access XML-RPC, so can attackers. The safest approach is disabling it entirely.
What should I monitor in XML-RPC logs?
Watch for: failed authentication attempts, repeated calls to the same method, calls from unusual IPs, and attacks using the system.multicall method. WP HealthKit's audit can help interpret these patterns.
Does the WordPress REST API have the same vulnerabilities?
The REST API is more modern and has better built-in security, but it's not immune to attacks. Apply the same principles: require authentication, implement rate limiting, monitor unusual activity, and restrict available methods.
XML-RPC Discovery and Fingerprinting
Attackers often scan for XML-RPC availability to map vulnerable sites. Even if XML-RPC is disabled, disclosure that it exists helps attackers fingerprint the site. The wp-xmlrpc.php endpoint's existence can be detected through HTTP requests. Advanced hardening removes any indication that XML-RPC is available, making reconnaissance more difficult.
Conclusion
WordPress XML-RPC is a legacy API that provides attack opportunities disproportionate to its usefulness. The safest approach is disabling it entirely if you don't actively need it. If you do need XML-RPC functionality, implement comprehensive hardening: rate limiting, IP whitelisting, method filtering, and detailed logging.
The implementation patterns here—selective method enabling, role-based filtering, request logging, and bypass detection—represent production-ready security measures.
However, XML-RPC security is often overlooked until it's exploited. WP HealthKit automatically checks whether your plugin properly handles XML-RPC, detects unprotected methods, and verifies that rate limiting actually prevents attacks.
Upload your plugin to WP HealthKit for a detailed security audit including XML-RPC analysis, identification of exposed methods, and specific recommendations for securing your implementation.