Introduction
The WordPress xmlrpc.php endpoint is a common target for brute force attacks and DDoS amplification. Attackers exploit the system.multicall method to send thousands of authentication requests in a single HTTP POST, consuming server CPU, memory, and bandwidth:
# Check for XML-RPC attack in access logs
grep "POST /xmlrpc.php" /var/log/nginx/access.log | wc -l
# May show thousands of requests per minute from a single IPThis can bring down a WordPress site by exhausting PHP workers and database connections.
Symptoms
- Server CPU usage spikes to 100% during attack periods
- PHP-FPM worker processes all occupied handling xmlrpc.php requests
- Access log shows massive volume of POST requests to
/xmlrpc.php - Site becomes unresponsive during attack due to resource exhaustion
- Bandwidth usage increases dramatically from large XML-RPC responses
Common Causes
xmlrpc.phpis enabled by default in all WordPress installations- Attackers use the
system.multicallmethod to bundle hundreds of requests - Pingback feature can be exploited for DDoS amplification against third-party sites
- No rate limiting on the XML-RPC endpoint
- XML-RPC is not needed for modern WordPress sites using the REST API
Step-by-Step Fix
- 1.Block xmlrpc.php at the Nginx level (most effective):
- 2.```nginx
- 3.location = /xmlrpc.php {
- 4.deny all;
- 5.access_log off;
- 6.log_not_found off;
- 7.return 444;
- 8.}
- 9.
` - 10.The
444status code closes the connection without sending a response, minimizing resource usage. - 11.Block at the Apache level using
.htaccessor VirtualHost: - 12.```apache
- 13.<Files "xmlrpc.php">
- 14.Require all denied
- 15.</Files>
- 16.
` - 17.Disable XML-RPC via WordPress filter (if Nginx/Apache blocking is not possible):
- 18.```php
- 19.// Add to mu-plugin or theme functions.php
- 20.add_filter('xmlrpc_enabled', '__return_false');
- 21.
` - 22.Allow XML-RPC only for specific IPs (e.g., Jetpack, mobile app):
- 23.```nginx
- 24.location = /xmlrpc.php {
- 25.allow 192.0.80.0/20; # Jetpack IP range
- 26.allow 192.0.96.0/20;
- 27.allow 192.0.112.0/20;
- 28.allow 2a02:ffc0::/29;
- 29.deny all;
fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } ```
- 1.Block pingback requests specifically to prevent DDoS amplification:
- 2.```apache
- 3.<FilesMatch "^(xmlrpc\.php)">
- 4.Order Deny,Allow
- 5.Deny from all
- 6.</FilesMatch>
- 7.# Or in Apache config, remove the pingback header
- 8.Header unset X-Pingback
- 9.
` - 10.And in WordPress:
- 11.```php
- 12.add_filter('wp_headers', function($headers) {
- 13.unset($headers['X-Pingback']);
- 14.return $headers;
- 15.});
- 16.
` - 17.Monitor and block attack sources using fail2ban:
- 18.```ini
- 19.# /etc/fail2ban/filter.d/wordpress-xmlrpc.conf
- 20.[Definition]
- 21.failregex = ^<HOST> .*POST .*/xmlrpc\.php.*$
- 22.ignoreregex =
# /etc/fail2ban/jail.local [wordpress-xmlrpc] enabled = true filter = wordpress-xmlrpc action = iptables-multiport[name=wordpress, port="http,https"] logpath = /var/log/nginx/access.log maxretry = 3 findtime = 60 bantime = 3600 ```
Prevention
- Disable XML-RPC on all WordPress sites that do not require it (most modern sites)
- Block
/xmlrpc.phpat the web server level as a default security measure - Monitor access logs for XML-RPC request spikes using automated alerting
- Use the REST API instead of XML-RPC for programmatic WordPress access
- Keep fail2ban configured and running to automatically block attack sources
- If you need XML-RPC for specific services (Jetpack, mobile app), restrict access to known IP ranges only
- Add XML-RPC blocking to your WordPress hardening checklist for every new installation