Rate limiting xmlrpc requests on WordPress using Nginx

WordPress based sites are target for most automated bots. Those bots look for various vulnerability in WordPress core, the themes, and the plugins. Then, there are some kids (and their kid bots) that target specific resources in a WP site. “xmlrpc.php” is one such resource. It uses XML-RPC protocol that does many things in WordPress. For example, it helps with remote sites to notify their mentions, to publish (and edit) articles using an app (such as WordPress app for Android / iOS). It is also used by many plugins such as Jetpack.

Naturally, xmlrpc.php file may be called multiple times in a day or in an hour (on a busy site). It may be called multiple times for every minute on a high traffic site. I have seen xmlrpc.php being accessed more frequently even on a site with no traffic too. Those traffic are likely from scan / scam bots, looking for vulnerabilities.

Since, there is no way to cache requests to xmlrpc.php, PHP and MySQL usage tend to go high quickly as every request needs a bit of php and MySQL. As a result, CPU usage spikes up, resulting in a wastage of precise CPU minutes. If we use platforms like AWS EC2, GCP, or Microsoft Azure where every CPU hour is charged, the cost of running a site can increase substantially.

In order to reduce the CPU usage, one solution is to completely block access to xmlrpc.php file. However, since this file is used for genuine purposes too, it is not recommended to disable access to this file. Alternatively, we can rate limit the requests to this file. A genuine request would not call this file multiple times per second. A decent limit is 1 request per second.

Let’s see how to implement rate limiting for xmlrpc in Nginx…

limit_req_zone $binary_remote_addr zone=wp_xmlrpc_limit:10m rate=1r/s;

server {
    server_name example.com;

    location = /xmlrpc.php {
        limit_req zone=wp_xmlrpc_limit;

        fastcgi_split_path_info ^(.+\.php)(/.+)$;

        if (!-f $document_root$fastcgi_script_name) { return 404; }

        # Mitigate https://httpoxy.org/ vulnerabilities
        fastcgi_param HTTP_PROXY "";

        include                     fastcgi_params;
        fastcgi_index               index.php;
        fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_pass                fpm;
    }

    # other location blocks such as location / {}
}

Basically, we define a separate location block to process xmlrpc.php file and then insert two lines of code to introduce rate limit. The first line (starting with limit_req_zone) should be defined outside of server block. The other one (starting with limit_req) should be defined inside the newly introduced location block.

In the above code, we limited the requests at 1 request per second. We can fine-tune it depending on our use-case. There are other areas to fine-tune too such as implementing a separate log for xmlrpc requests. That’s for another day!

Happy Hosting!

css.php