Protecting WordPress XML-RPC: Security Risks and Solutions
Understand XML-RPC security risks in WordPress and learn how to protect or disable this protocol to prevent brute force and DDoS attacks.
XML-RPC is a remote procedure call protocol that allows external applications to communicate with WordPress. While useful for some applications, it presents significant security risks if left unprotected.
What is XML-RPC?
XML-RPC enables remote publishing through:
- WordPress mobile apps
- Jetpack plugin connectivity
- Remote posting applications
- Pingbacks and trackbacks
- Desktop blogging clients
Security Risks
XML-RPC vulnerabilities include:
- Brute force amplification attacks
- DDoS through pingback abuse
- Credential harvesting attempts
- System information disclosure
Brute Force Amplification
XML-RPC allows testing multiple passwords in one request:
// Attacker can test 500 passwords in single request
// via system.multicall method
// This bypasses traditional brute force protection
Completely Disabling XML-RPC
If you do not need XML-RPC functionality:
// Disable XML-RPC completely
add_filter('xmlrpc_enabled', '__return_false');
// Also remove XML-RPC link from header
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
// Block access via .htaccess
// Add to WordPress root .htaccess:
//
// Order Deny,Allow
// Deny from all
//
Selective Method Blocking
Block dangerous methods while keeping useful ones:
// Block specific XML-RPC methods
add_filter('xmlrpc_methods', function($methods) {
// Block pingbacks (DDoS vector)
unset($methods['pingback.ping']);
unset($methods['pingback.extensions.getPingbacks']);
// Block credential testing methods
unset($methods['system.multicall']);
unset($methods['system.listMethods']);
unset($methods['system.getCapabilities']);
// Block user enumeration
unset($methods['wp.getUsers']);
unset($methods['wp.getAuthors']);
return $methods;
});
Rate Limiting XML-RPC
Implement request throttling:
function rate_limit_xmlrpc() {
if (strpos($_SERVER['REQUEST_URI'], 'xmlrpc.php') === false) {
return;
}
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'xmlrpc_requests_' . md5($ip);
$requests = get_transient($key) ?: 0;
// Allow only 5 XML-RPC requests per minute
if ($requests > 5) {
header('HTTP/1.1 429 Too Many Requests');
header('Retry-After: 60');
exit('Rate limit exceeded');
}
set_transient($key, $requests + 1, MINUTE_IN_SECONDS);
}
add_action('init', 'rate_limit_xmlrpc', 1);
Authentication Enhancement
Require stronger authentication for XML-RPC:
// Require application passwords for XML-RPC
add_filter('xmlrpc_enabled', function($enabled) {
if (!isset($_SERVER['PHP_AUTH_USER'])) {
return false;
}
$user = wp_authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
if (is_wp_error($user)) {
return false;
}
// Log XML-RPC authentication
error_log(sprintf(
'XML-RPC auth: User %s from IP %s',
$user->user_login,
$_SERVER['REMOTE_ADDR']
));
return $enabled;
});
IP Whitelisting
Allow XML-RPC only from trusted IPs:
function whitelist_xmlrpc_ips() {
if (strpos($_SERVER['REQUEST_URI'], 'xmlrpc.php') === false) {
return;
}
$allowed_ips = array(
'192.168.1.100', // Office IP
'10.0.0.0/8', // Internal network
'203.0.113.50', // Automation server
);
$client_ip = $_SERVER['REMOTE_ADDR'];
$allowed = false;
foreach ($allowed_ips as $ip) {
if (strpos($ip, '/') !== false) {
// CIDR notation
if (cidr_match($client_ip, $ip)) {
$allowed = true;
break;
}
} else {
if ($client_ip === $ip) {
$allowed = true;
break;
}
}
}
if (!$allowed) {
header('HTTP/1.1 403 Forbidden');
exit('Access denied');
}
}
add_action('init', 'whitelist_xmlrpc_ips', 1);
Monitoring XML-RPC Activity
Track and alert on suspicious activity:
function monitor_xmlrpc_activity() {
if (strpos($_SERVER['REQUEST_URI'], 'xmlrpc.php') === false) {
return;
}
$log_entry = array(
'time' => current_time('mysql'),
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
'method' => $_SERVER['REQUEST_METHOD'],
);
// Get request body for method detection
$body = file_get_contents('php://input');
if (preg_match('/([^<]+)<\/methodName>/', $body, $matches)) {
$log_entry['xmlrpc_method'] = $matches[1];
}
// Check for multicall (brute force indicator)
if (isset($log_entry['xmlrpc_method']) && $log_entry['xmlrpc_method'] === 'system.multicall') {
wp_mail(
get_option('admin_email'),
'XML-RPC Brute Force Attempt',
sprintf(
'Multicall request detected from IP: %s',
$log_entry['ip']
)
);
}
$logs = get_option('xmlrpc_access_log', array());
array_unshift($logs, $log_entry);
$logs = array_slice($logs, 0, 500);
update_option('xmlrpc_access_log', $logs);
}
add_action('init', 'monitor_xmlrpc_activity', 0);
Jetpack Compatibility
Keep XML-RPC for Jetpack while blocking attacks:
function jetpack_xmlrpc_filter($enabled) {
// Allow Jetpack IP ranges
$jetpack_ips = array(
'122.248.245.244',
'54.217.201.243',
'54.232.116.4',
// Add current Jetpack IPs from their documentation
);
$client_ip = $_SERVER['REMOTE_ADDR'];
// Allow if from Jetpack
if (in_array($client_ip, $jetpack_ips)) {
return true;
}
// Check for Jetpack user agent
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (strpos($ua, 'Jetpack') !== false) {
return true;
}
// Block all other XML-RPC
return false;
}
add_filter('xmlrpc_enabled', 'jetpack_xmlrpc_filter');
Server-Level Block
Block XML-RPC at the server level for best performance:
# Apache .htaccess
<Files xmlrpc.php>
<RequireAll>
Require ip 192.168.1.0/24
Require ip 10.0.0.0/8
</RequireAll>
</Files>
# Nginx configuration
location = /xmlrpc.php {
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
}
Conclusion
Unless you specifically need XML-RPC for mobile apps or remote publishing, disable it completely. If required, implement strict IP whitelisting, rate limiting, and monitoring to minimize attack surface.
Written by Sarah Chen
WP Folder Shield Team