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:

  1. Monitor the API: Don’t just watch your homepage; watch your /wp-json/ endpoints.

  2. Use 429 Status: It’s the “polite” way to tell a bot to go away without crashing your own service.

  3. Work with your Panel: If you use cPanel or Plesk, always use the Include directories to ensure your hard work isn’t wiped out during an update.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *