# Dynamic Denylisting of IP Addresses
Type of document: How-to guide
Product: NGINX Plus
> Control access to your site or apps from specific client IP addresses, using dynamic denylists built with the F5 NGINX Plus key-value store and API.
---
This section describes how to create a denylist or allowlist of specific client IP addresses, which denies or allows them access to your site, and how to dynamically maintain the list of addresses.
## Overview
In F5 NGINX Plus [Release 13](nginx/releases.md#r13) (R13) and later, you can denylist some IP addresses as well as create and maintain a database of denylisted IP addresses. You can also explicitly allowlist other IP addresses. The IP addresses database is managed with the NGINX Plus API and keyval modules.
NGINX Plus [Release 19](nginx/releases.md#r19) extends this capability by matching an IP address to any address within a subnet or network range.
## Prerequisites
NGINX Plus [Release 13](nginx/releases.md#r13) and later, NGINX Plus [Release 19](nginx/releases.md#r19) and later for network ranges support.
## Setup
First, enable the database for storing the list of denylisted and allowlisted IP addresses.
1. In NGINX Plus configuration file, include the [keyval_zone](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval_zone) directive in the [http](https://nginx.org/en/docs/http/ngx_http_core_module.html#http) context to create a memory zone for storing keys and values. This sample directive creates a 1‑MB zone called **one**.
```nginx
http {
# ...
keyval_zone zone=one:1m;
}
```
To perform matching of an IP address against subnets (for example, `192.168.13.0/24`), specify the `type=ip` parameter of the [keyval_zone](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval_zone) directive:
```nginx
http {
# ...
keyval_zone zone=one:1m type=ip;
}
```
Note that the size of [keyval_zone](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval_zone) should also be increased as the `type=ip` parameter also enables an extra index stored in the zone.
You can optionally include the `state` parameter to create a file where the key‑value database is stored and so persists across NGINX Plus reloads and restarts; in this example, **one.keyval**:
```nginx
keyval_zone zone=one:1m state=one.keyval;
```
2. Enable the NGINX Plus API in read‑write mode with the [api](https://nginx.org/en/docs/http/ngx_http_api_module.html#api) directive:
```nginx
# ...
server {
listen 80;
server_name www.example.com;
location /api {
api write=on;
}
}
```
We strongly recommend [restricting access](/nginx/admin-guide/security-controls/controlling-access-proxied-http.md) to this location, for example by allowing access only from `localhost` (`127.0.0.1`), and by using HTTP basic authentication to restrict use of the `PATCH`, `POST`, and `DELETE` methods to a specified set of users:
```nginx
# ...
server {
listen 80;
server_name www.example.com;
location /api {
api write=on;
allow 127.0.0.1;
deny all;
limit_except GET {
auth_basic "NGINX Plus API";
auth_basic_user_file /path/to/passwd/file;
}
}
}
```
3. Populate the key‑value database with the API's [POST](https://nginx.org/en/docs/http/ngx_http_api_module.html#postHttpKeyvalZoneData) method, supplying the data in JSON format. You can use the `curl` command as in the following example. If the zone is empty, you can enter several key‑value pairs at once; otherwise, pairs must be added one at a time.
```shell
$ curl -X POST -d '{
"10.0.0.1": "1",
"10.0.0.2": "1",
"10.0.0.3": "0",
"10.0.0.4": "0"
}' -s http://www.example.com/api/6/http/keyvals/one
```
If you have specified matching of IP addresses against network ranges (with the `type=ip` parameter of the [keyval_zone](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval_zone) directive), send the `POST` command with the network range specified in CIDR notation:
```shell
$ curl -X POST -d '{
"192.168.13.0/24": "1"
}' -s http://www.example.com/api/6/http/keyvals/one
```
4. Define how client IP addresses are evaluated against the key‑value database, by including the [keyval](https://nginx.org/en/docs/http/ngx_http_keyval_module.html#keyval) directive in the [http](https://nginx.org/en/docs/http/ngx_http_core_module.html#http) context.
The directive takes advantage of the standard NGINX and NGINX Plus variable [`$remote_addr`](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_remote_addr), which is set to the client IP address automatically for every request.
As it processes each request, NGINX Plus:
- Looks up the first parameter (here, `$remote_addr`, preset to the client's IP address) in the key‑value database specified by the `zone=` parameter (here, **one**).
- If a key in the database exactly matches `$remote_addr`, sets the second parameter (here, `$target`) to the value corresponding to the key. In our example, the value is `1` for denylisted addresses or `0` for allowlisted addresses.
```nginx
http {
# ...
keyval_zone zone=one:1m type=ip state=one.keyval;
keyval $remote_addr $target zone=one; # Client address is the key,
# $target is the value;
}
```
5. Create a rule with the [if](https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if) directive that either allows or denies access depending on the client IP address. With this rule, access is allowed when `$target` is `0` and denied when it is `1`:
```nginx
if ($target) {
return 403;
}
```
## Managing the Key-Value Database
You can use API methods to update a key‑value database dynamically, without requiring a reload of NGINX Plus.
All of the following examples operate on the **one** zone, which is accessible at ****.
- To get the list of all database entries for a zone:
```shell
curl -X GET 'http://www.example.com/api/6/http/keyvals/one'
```
- To update the value for an existing entry (in this example to change the access status for IP address `10.0.0.4` from allowlisted to denylisted):
```shell
curl -X PATCH -d '{"10.0.0.4": "1"}' -s 'http://www.example.com/api/6/http/keyvals/one'
```
- To add an entry to a populated zone:
```shell
curl -X POST -d '{"10.0.0.5": "1"}' -s 'http://www.example.com/api/6/http/keyvals/one'
```
- To delete an entry:
```shell
curl -X PATCH -d '{"10.0.0.4":null}' -s 'http://www.example.com/api/6/http/keyvals/one'
```
## Full Example
The full NGINX Plus configuration:
```nginx
http {
# ...
keyval_zone zone=one:1m type=ip state=one.keyval;
keyval $remote_addr $target zone=one;
server {
listen 80;
server_name www.example.com;
location /api {
api write=on;
allow 127.0.0.1;
deny all;
limit_except GET {
auth_basic "NGINX Plus API";
auth_basic_user_file /path/to/passwd/file;
}
}
if ($target) {
return 403;
}
}
}
```
This configuration:
- Creates a 1 MB keyval zone **one** that accepts network ranges and also creates the file **one.keyval** to make the database of key‑value pairs persists across reloads and restarts of NGINX Plus.
- Enables the NGINX Plus API in write mode so that the zone can populated with IP addresses.
- Enables lookup of the IP address `$remote_addr` in the key-value database as the key, and puts the value of the found key into the `$target` variable.
- Enables a simple rule to check for the resulting value: if the value of `$target` is `1` (address is denylisted), return `403 (Forbidden)` to the client.
The following `curl` command populates the empty keyval zone **one** with IP addresses that are denylisted (value is `1`) or allowlisted (value is `0`):
```shell
curl -X POST -d '{
"10.0.0.1": "1",
"192.168.13.0/24": "1",
"10.0.0.3": "0",
"10.0.0.4": "0"
}' -s 'http://www.example.com/api/6/http/keyvals/one'
```
## See Also
- [Dynamic IP Denylisting with NGINX Plus and fail2ban](https://www.nginx.com/blog/dynamic-ip-denylisting-with-nginx-plus-and-fail2ban/)