---
title: How to Rate Limit Requests to Specific PHP Files with Nginx
date: 2024-09-14T11:01:20+00:00
modified: 2025-01-02T16:24:45+00:00
image:: https://kaspars.net/wp-content/uploads/2024/09/nginx-rate-limit-php.png
permalink: https://kaspars.net/blog/nginx-rate-limit-php
post_type: post
author:
  name: Kaspars
  avatar: https://reverse.kaspars.net/gravatar/avatar/92bfcd3a8c3a21a033a6484d32c25a40b113ec6891f674336081513d5c98ef76?s=96&d=mm&r=g
post_tag:
  - How to
  - PHP
  - Snippet
category:
  - Linux
  - WordPress
---

# How to Rate Limit Requests to Specific PHP Files with Nginx

WordPress login and XML-RPC endpoints at `wp-login.php` and `xmlrpc.php` respectively are the mostly widely abused targets for bots scanning the web for sites with weak passwords. Here is a simple Nginx configuration snippet that allows you to set the `limit_req` depending on the request path and method.

First, define the `<a href="https://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone">limit_req_zone</a>` at the `http {}` level and conditionally enable it **only for `POST` requests to the desired request URIs** by setting the limit request zone input to `<a href="https://nginx.org/en/docs/http/ngx_http_core_module.html#var_binary_remote_addr">$binary_remote_addr</a>` while keeping it undefined for all the un-mapped requests:

```
map "$request_method:$uri" $wp_post_limit_key {
	POST:/wp-login.php $binary_remote_addr;
	POST:/xmlrpc.php $binary_remote_addr;
}

limit_req_zone $wp_post_limit_key zone=wp_post_limit_zone:10m rate=5r/m;
```

where:

- `$wp_post_limit_key` is our custom variable name that is either unset by default or equal to `$binary_remote_addr` when the current request should be rate-limited.
- `wp_post_limit_zone` is our custom zone name that must be referenced in `limit_req` of each `server {}` block.
- `rate=5r/m` sets the allowed request rate from the same IP to one request per 12 seconds (60/5). **Importantly, as mentioned by Igor in the comments, this is the allowed interval between requests instead of total amount of allowed requests per interval.**

And then add it to the PHP location in each of your `server {}` blocks:

```
location ~ \.php$ {
	try_files $uri =404;
        limit_req zone=wp_post_limit_zone;
	// ...
}
```

You can define multiple `limit_req_zone` configurations for different request mapping logic and apply them all to the same PHP location block.

## Adjust the HTTP Response Code

By default Nginx responds with HTTP [503 Service Unavailable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) status code to all throttled requests. Use the `limit_req_status` directive to change it to [429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) in either `http {}` or `server {}` block:

```
limit_req_status 429; 
```