# 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/)