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:

bash
# 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 IP

This 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.php is enabled by default in all WordPress installations
  • Attackers use the system.multicall method 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. 1.Block xmlrpc.php at the Nginx level (most effective):
  2. 2.```nginx
  3. 3.location = /xmlrpc.php {
  4. 4.deny all;
  5. 5.access_log off;
  6. 6.log_not_found off;
  7. 7.return 444;
  8. 8.}
  9. 9.`
  10. 10.The 444 status code closes the connection without sending a response, minimizing resource usage.
  11. 11.Block at the Apache level using .htaccess or VirtualHost:
  12. 12.```apache
  13. 13.<Files "xmlrpc.php">
  14. 14.Require all denied
  15. 15.</Files>
  16. 16.`
  17. 17.Disable XML-RPC via WordPress filter (if Nginx/Apache blocking is not possible):
  18. 18.```php
  19. 19.// Add to mu-plugin or theme functions.php
  20. 20.add_filter('xmlrpc_enabled', '__return_false');
  21. 21.`
  22. 22.Allow XML-RPC only for specific IPs (e.g., Jetpack, mobile app):
  23. 23.```nginx
  24. 24.location = /xmlrpc.php {
  25. 25.allow 192.0.80.0/20; # Jetpack IP range
  26. 26.allow 192.0.96.0/20;
  27. 27.allow 192.0.112.0/20;
  28. 28.allow 2a02:ffc0::/29;
  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. 1.Block pingback requests specifically to prevent DDoS amplification:
  2. 2.```apache
  3. 3.<FilesMatch "^(xmlrpc\.php)">
  4. 4.Order Deny,Allow
  5. 5.Deny from all
  6. 6.</FilesMatch>
  7. 7.# Or in Apache config, remove the pingback header
  8. 8.Header unset X-Pingback
  9. 9.`
  10. 10.And in WordPress:
  11. 11.```php
  12. 12.add_filter('wp_headers', function($headers) {
  13. 13.unset($headers['X-Pingback']);
  14. 14.return $headers;
  15. 15.});
  16. 16.`
  17. 17.Monitor and block attack sources using fail2ban:
  18. 18.```ini
  19. 19.# /etc/fail2ban/filter.d/wordpress-xmlrpc.conf
  20. 20.[Definition]
  21. 21.failregex = ^<HOST> .*POST .*/xmlrpc\.php.*$
  22. 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.php at 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