Protecting wp-admin Brute Force Attacks with nginx Rate Limiting

WordPress is gaining in popularity and therefore the number of attacks specific to WordPress keep increasing. I manage servers for some political websites which get attacked relentlessly so that means I get to create new firewall rules and rate limiting directives to help keep the site running – very fun!

Recently this site was being attacked by a bot that was trying different wp-admin requests as a DoS attack (Denial of Service) that were nonsense like wp-admin/1 wp-admin/?herp which of course, all bypass any server-side caching I had configured and were being executed to quickly that it was sucking up tons of resources on the dedicated server. In this post you will learn how to protect the wp-admin of any WordPress or WooCommerce site using nginx rate-limiting. I recommend coupling this with fail2ban as well as outlined here.

Throttle wp-admin Attacks with nginx Rate Limiting

In the http { block you add this directive so nginx has a zone for your rate limiting throttle to protect the WordPress admin area in wp-admin with this directive. I add this in /etc/nginx/nginx.conf on Ubuntu and Debian systems.

# DoS Protection set zone
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

Now we add the protection to the virtual host file typically located in the
/etc/nginx/sites-available folder In the server { we need to add this block to handle the wp-admin requests with PHP and also make sure the throttle zone is active there too. If you have a large amount of legitimate wp-admin requests you may have to increase the burst value higher than 3 (in burst=3 below).

If this part is not done correctly for the wp-admin protection you may get a 502 bad gateway error or a File not found error from nginx, the block below should fix this for you. Make sure to use the correct fastcgi_pass parameter which you can find in your location ~ \.php$ { block.

# throttle wp-admin requests
location /wp-admin/ {
    limit_req zone=one burst=3 nodelay;
    limit_req_status 444;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php7.2-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    try_files $uri $uri/ /wp-admin/index.php?q=$uri&$args;
}

You will need to reload nginx after applying these changes

sudo service nginx reload

Now make sure to browse around your wp-admin area to make sure it works 🙂

Sources

Why use define burst if nodelay is set?
Primary script unknown error
wp-admin goes to 404 but wp-admin/index.php does not