🛡️ NGINX Rate Limiting Configuration

Comprehensive Security Guide for WordPress Sites

angelscript

📋 Overview

This guide provides comprehensive instructions for implementing rate limiting and advanced security measures in NGINX to protect WordPress sites from brute force attacks and malicious traffic.

🎯 Key Objectives:
  • Implement server-level rate limiting
  • Protect wp-login.php and xmlrpc.php endpoints
  • Configure HTTP 444 responses for malicious requests
  • Optimize security without affecting legitimate users

🔒 What is Rate Limiting?

Rate limiting is a security mechanism that controls the number of GET and POST requests a user can make within a specific time period. This technique is essential for:

  • Slowing down brute force attacks by limiting incoming request rates to values typical for real users
  • Protecting server resources by intercepting malicious requests before they reach WordPress
  • Preventing server downtime caused by overwhelming request volumes
  • Maintaining site performance for legitimate users

Rate Limiting Flow

Client Request
NGINX Rate Check
Within Limit?
Allow/Block

⚠️ Understanding XML-RPC Vulnerability

XML-RPC is a bundled WordPress script that enables remote connections to WordPress sites. While it provides valuable functionality for remote publishing tools, it also presents a significant security risk:

Security Concerns:
  • Malicious bots actively scan for wp-login.php and xmlrpc.php
  • Automated scripts attempt countless login attempts
  • Excessive requests can crash your server
  • Brute force attacks can compromise site security

By implementing rate limiting on both wp-login.php and xmlrpc.php, we can effectively stop brute force attacks without interfering with legitimate user access.

🔧 HTTP 444 Response Code

We utilize the limit_req_status directive configured to return HTTP 444 responses. This is a non-standard response code specific to NGINX:

Response Code Description Use Case
HTTP 444 Connection closed without response Suspected malware/malicious attacks
Standard HTTP Returns error page Normal error handling
Why HTTP 444?

When NGINX returns a 444 status, it simply drops the connection without sending any response. This conserves server resources and provides no information to attackers about the server configuration.

⚙️ Configuration Steps

Step 1: Configure Rate Limiting in nginx.conf

First, navigate to the NGINX configuration directory and edit the main configuration file:

cd /etc/nginx
sudo nano nginx.conf

Add the following directive in the HTTP context, underneath the gzip settings:

##

Rate Limiting
limit_req_zone $binary_remote_addr zone=wp:10m rate=30r/m;
less

Understanding the Directive Parameters:

Parameter Value Description
limit_req_zone - Defines rate limiting parameters
$binary_remote_addr - Uses binary representation of client IP address
zone=wp:10m wp / 10MB Shared memory zone name and size (stores ~160,000 IPs)
rate=30r/m 30 requests/min Maximum request rate (1 request every 2 seconds)
💡 Memory Allocation:

One megabyte can store approximately 16,000 unique IP addresses. For high-traffic sites, consider increasing the zone size. The example uses 10MB, which can handle ~160,000 unique IPs.

⚠️ Customizing Rate Limits:
  • 30r/m = 1 request every 2 seconds
  • 10r/m = 1 request every 6 seconds (more restrictive)
  • Adjust based on your site's legitimate traffic patterns

Save your changes and test the configuration:

sudo nginx -t

Note: Do not reload NGINX yet; we'll complete the configuration first.

Step 2: Create Rate Limiting Include File

Navigate to the includes directory and create a site-specific rate limiting configuration:

cd /etc/nginx/includes/
sudo nano rate_limiting_example.com.conf

Add the following configuration (replace example.com with your domain):

location = /wp-login.php {
limit_req zone=wp burst=20 nodelay;
limit_req_status 444;
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-MODIFY.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;

}

location = /xmlrpc.php {
limit_req zone=wp burst=20 nodelay;
limit_req_status 444;
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-MODIFY.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}
xml

Directive Breakdown:

Directive Purpose
location = Exact match for specified file path
limit_req zone=wp References the shared memory zone defined in nginx.conf
burst=20 Allows 20 requests to exceed rate limit before rejection
nodelay Immediately drops requests exceeding the limit
limit_req_status 444 Returns HTTP 444 (connection closed)

Step 3: Configure PHP-FPM Socket Path

You need to replace MODIFY with your actual PHP-FPM pool socket name. First, retrieve your socket path from your server block configuration:

cd /etc/nginx/sites-available/
sudo nano example.com.conf

Locate the fastcgi_pass directive and copy the socket path. For example:

fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;

Copy the unique identifier (e.g., -example.com) and return to your rate limiting file:

cd /etc/nginx/includes/
sudo nano rate_limiting_example.com.conf

Replace both instances of MODIFY with your socket identifier:

fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;

Save the file and verify the full path:

readlink -f rate_limiting_example.com.conf

Copy the complete path for the next step.

Step 4: Include Rate Limiting in Server Block

Edit your site's server block configuration:

cd /etc/nginx/sites-available/
sudo nano example.com.conf

Locate the PHP processing location block and add the include directive immediately before it:

# Rate Limiting Include

include /etc/nginx/includes/rate_limiting_example.com.conf;

location ~ .php$ {
include snippets/fastcgi-php.conf;
fastcgi_param HTTP_HOST $host;
fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;
include /etc/nginx/includes/fastcgi_optimize.conf;
}
angelscript

Configuration File Structure

nginx.conf
(Global Zone)
rate_limiting.conf
(Site Specific)
server block
(Include)

Step 5: Test and Enable Configuration

Always test your configuration before reloading NGINX:

sudo nginx -t
⚠️ Common Error: Invalid Number of Arguments

If you encounter the error "invalid number of arguments in fastcgi_pass directive", check for spacing issues in your rate limiting configuration file. Specifically, ensure there are no spaces between the colon and slash in the socket path:

  • ❌ Incorrect: unix: /run/php/... (space after colon)
  • ✅ Correct: unix:/run/php/... (no space)

Use the -L flag to display line numbers when editing:

sudo nano -L /etc/nginx/includes/rate_limiting_example.com.conf

Once the configuration test passes successfully, reload NGINX:

sudo systemctl reload nginx
✅ Configuration Complete!

Rate limiting is now active for wp-login.php and xmlrpc.php. Your WordPress site is now protected against brute force attacks while maintaining seamless access for legitimate users.

📊 Rate Limiting in Action

Request Flow Diagram

Legitimate User Scenario:

Request 1
✓ Allowed
Wait 2s
Request 2
✓ Allowed

Brute Force Attack Scenario:

Requests 1-20
✓ Burst
Request 21+
✗ HTTP 444

🎯 Best Practices

Optimization Tips:
  • Monitor Logs: Regularly check NGINX logs to identify blocked requests and adjust rates if needed
  • Adjust Burst Values: The burst parameter allows temporary spikes in legitimate traffic
  • Zone Sizing: For high-traffic sites, increase the zone size beyond 10m
  • Multiple Zones: Create separate zones for different security levels
  • Test Thoroughly: Verify that legitimate users can access your site without issues

Advanced Configuration Example:

# Different rate limits for different endpoints

limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/m;
angelscript

🔍 Troubleshooting

Issue Solution
Configuration test fails Check for syntax errors, especially spacing in socket paths
Legitimate users blocked Increase rate limit or burst value
Rate limiting not working Verify zone name matches in both nginx.conf and include files
Server performance issues Increase shared memory zone size

📚 Additional Resources

  • Monitor rate limiting effectiveness through NGINX access logs
  • Consider implementing fail2ban for additional IP-based blocking
  • Regularly update security configurations based on traffic patterns
  • Test configuration changes in a staging environment first