Rate limiting third party app that spams thousands of requests
How to Stop WooCommerce REST API Abuse Using Nginx Rate Limiting
If your WordPress site is feeling sluggish, but your security plugins (like Wordfence or Sucuri) show zero attacks, you might be looking in the wrong place.
Modern “bots” aren’t always trying to brute-force your login page. Instead, they often target the WooCommerce REST API (/wp-json/wc/v3/). Because these requests often bypass traditional PHP-based security layers, they hit your database hard, spiking CPU usage and slowing down your store for real customers.
Here is how we identified, throttled, and permanently blocked this behavior at the server level.
1. The Discovery: Hunting the Bot
The first step is checking your Nginx access logs. We noticed a single IP address hitting the orders endpoint thousands of times per hour.
You can check your own logs for a specific “offender” IP with this command:
Bash
grep "OFFENDER_IP" /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c
The result? We saw thousands of 200 OK status codes for the same API path. This confirmed the bot was successfully draining server resources.
2. The Solution: Nginx limit_req
To fix this, we implemented a “Leaky Bucket” algorithm. This allows a few legitimate requests to pass through in a “burst,” but forces a hard ceiling on how many requests a single IP can make per minute.
Step A: Define the Zone
In the global -node="16" data-index-in-node="14">nginx.conf (or a dedicated includes file), we defined the limit zone:
Nginx
limit_req_zone $binary_remote_addr zone=bot_limit:10m rate=4r/m;
-
rate=4r/m: This limits the IP to roughly 240 requests per hour.
-
zone=bot_limit: The name we’ll use to call this rule later.
Step B: Apply to the API Path
We then targeted the specific WooCommerce API endpoint within the site’s server block:
Nginx
location ~ ^/wp-json/wc/v3/ {
limit_req zone=bot_limit burst=5 nodelay;
limit_req_status 429;
# Ensure the request still passes to your PHP/Apache backend
proxy_pass http://your_backend;
}
-
burst=5: Allows a small “cushion” of 5 rapid requests before the hammer drops.
-
429: This sends a “Too Many Requests” error back to the bot, saving your CPU from doing any heavy lifting.
3. The Trap: cPanel Configuration Persistence
If you are using cPanel, you might find that your manual edits to Nginx files disappear after a few hours. This is because cPanel’s rebuildnginxconf script overwrites files to keep them in sync with its internal database.
The Permanent Fix: Don’t edit the main .conf files. Instead, use cPanel’s “Include” system. Navigate to: /etc/nginx/conf.d/users/YOUR_USER/YOUR_DOMAIN/
Create a new file there (e.g., api_fix.conf) and put your location block inside it. This way, when cPanel rebuilds the config, it “stitches” your custom rule back into the main file automatically.
4. The Results: Watching the Shield Work
After applying the fix, we ran a final check on the logs:
Bash
grep "OFFENDER_IP" /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c
Old Stats: 2,690 (200 OK) / 0 (429 Errors) New Stats: 1,727 (200 OK) / 567 (429 Errors)
The “429” errors represent hundreds of hits that never reached the database. The server load stabilized, and the bot eventually gave up as it was no longer getting the data it wanted at high speed.
Key Takeaways:
-
Monitor the API: Don’t just watch your homepage; watch your
/wp-json/endpoints. -
Use 429 Status: It’s the “polite” way to tell a bot to go away without crashing your own service.
-
Work with your Panel: If you use cPanel or Plesk, always use the
Includedirectories to ensure your hard work isn’t wiped out during an update.