> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Blocking Traffic

> Configuring Nginx to allow or deny IPs and using fail2ban to automatically ban repeated failed HTTP Basic auth attempts

In this lesson you'll learn how to block and allow traffic for an example site (`example.com`) using Nginx `allow` / `deny` directives, and how to use [fail2ban](https://www.fail2ban.org/) to automatically ban IPs that repeatedly fail authentication. The site exposes a protected `/admin` endpoint that uses HTTP Basic authentication; you can restrict access by IP in Nginx and automatically ban attackers with fail2ban.

Key goals:

* Use Nginx `allow` and `deny` to permit or block specific IP addresses or CIDR ranges.
* Use [fail2ban](https://www.fail2ban.org/) to automatically block IPs that repeatedly submit bad credentials, avoiding large, manually maintained deny lists.

***

## Base Nginx configuration (HTTP -> HTTPS, TLS, headers, protected /admin)

Start with this base Nginx site configuration for `example.com`. It redirects HTTP to HTTPS, configures TLS, sets security headers, serves files from a document root, and protects `/admin` with HTTP Basic auth:

```nginx theme={null}
server {
    listen 80;

    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;

    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/certs/example.com-key.pem;

    root /var/www/html;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";
    add_header Referrer-Policy origin;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }

    location /admin {
        auth_basic "Restricted Access";
        auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
    }
}
```

This configuration is the baseline used for the rest of the examples below.

***

## Verify DNS/name resolution and test connectivity

On your client nodes (for example `node01` and `node02`) ensure `example.com` resolves to your Nginx server by editing `/etc/hosts`:

```text theme={null}
127.0.0.1        localhost
::1              localhost ip6-localhost ip6-loopback
192.231.128.3    node02
192.231.128.10   example.com
```

Check connectivity with curl. Because the TLS certificate was issued with a local CA (e.g., [mkcert](https://mkcert.dev)), curl will not trust it by default.

Example checks:

```bash theme={null}
# HTTP (redirects to HTTPS)
curl http://example.com

# HTTPS without trusting the CA: fails with certificate verification error
curl https://example.com
# Example error:
# curl: (60) SSL certificate problem: unable to get local issuer certificate
```

To bypass certificate verification for testing only, use `-k` (equivalent to `--insecure`):

<Callout icon="lightbulb" color="#1CB2FE">
  Using `-k` disables certificate verification. Only use it for testing; do not use it in production scripts.
</Callout>

```bash theme={null}
# Send a HEAD request and ignore cert verification
curl -k --head https://example.com

# Sample response headers (truncated)
# HTTP/1.1 200 OK
# Server: nginx/1.18.0 (Ubuntu)
# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# ...
```

If you request the protected `/admin` without credentials, Nginx returns `401 Unauthorized`:

```bash theme={null}
curl -k --head https://example.com/admin
# HTTP/1.1 401 Unauthorized
# WWW-Authenticate: Basic realm="Restricted Access"
```

***

## Manually block a single IP with `deny`

To block a specific IP (for example `node02` with IP `192.231.128.3`), add a `deny` directive in the `location /` block. Example:

```nginx theme={null}
location / {
    deny 192.231.128.3/32; # node02
    try_files $uri $uri/ =404;
}
```

After editing the Nginx site file, test and reload Nginx:

```bash theme={null}
nginx -t
nginx -s reload
```

Expected outcome:

* node01 (allowed) → receives `200 OK`.
* node02 (denied) → receives `403 Forbidden`.

***

## Allow only one IP to access `/admin` and deny all others

To permit a single management IP (e.g., `node01` at `192.231.128.12`) to access `/admin` and deny everyone else, use `allow` followed by `deny all` inside the `/admin` location:

```nginx theme={null}
location /admin {
    allow 192.231.128.12/32; # node01
    deny all;
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
}
```

Notes:

* Each `allow` and `deny` directive requires a trailing semicolon.
* If the allowed client supplies an invalid username/password, Nginx will still return `401 Unauthorized` because `auth_basic` runs after the allow/deny check.

***

## Allow a whole subnet (CIDR) instead of many single IPs

Instead of maintaining many `/32` entries, permit an entire subnet. For example, to allow the `192.231.128.0/24` network (which contains `node01` and `node02`), use:

```nginx theme={null}
location / {
    allow 192.231.128.0/24; # node01 and node02
    deny all;
    try_files $uri $uri/ =404;
}

location /admin {
    allow 192.231.128.0/24; # node01 and node02
    deny all;
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
}
```

CIDR quick reference:

| CIDR  | What it means                                                                                            |
| ----- | -------------------------------------------------------------------------------------------------------- |
| `/32` | Only the exact IP specified is allowed (single host).                                                    |
| `/24` | The first three octets are fixed; the last octet ranges 0–255 (e.g., `192.231.128.0`–`192.231.128.255`). |

Remember: include the trailing semicolon: `allow 192.231.128.0/24;`

***

## Why not maintain long deny lists? Use fail2ban to automate bans

Manually maintaining long `deny` lists in Nginx becomes cumbersome and error-prone. [fail2ban](https://www.fail2ban.org/) monitors logs (including Nginx error logs), detects repeated failures (such as failed HTTP Basic auth attempts), and adds temporary firewall rules (bans) for misbehaving IPs.

Install and configure fail2ban (Debian/Ubuntu example):

```bash theme={null}
sudo apt update -y
sudo apt install -y fail2ban
```

Create a local config so package updates don't overwrite your settings:

```bash theme={null}
cd /etc/fail2ban
sudo cp jail.conf jail.local
```

Enable and configure the `nginx-http-auth` jail by adding or editing a snippet in `/etc/fail2ban/jail.local`:

```ini theme={null}
[nginx-http-auth]
enabled    = true
port       = http,https
filter     = nginx-http-auth
logpath    = %(nginx_error_log)s
maxretry   = 1
bantime    = 600
```

fail2ban option meanings:

| Option     | Description                                                          |
| ---------- | -------------------------------------------------------------------- |
| `enabled`  | Enable this jail when `true`.                                        |
| `port`     | Ports to apply the ban to (`http`, `https`).                         |
| `filter`   | The filter name (matches patterns in logs).                          |
| `logpath`  | Path to the Nginx error log where failed auth attempts are recorded. |
| `maxretry` | Number of failures before banning (`1` = ban after one failure).     |
| `bantime`  | Duration of the ban in seconds (`600` = 10 minutes).                 |

<Callout icon="warning" color="#FF6B6B">
  Setting `maxretry = 1` will ban after a single failed authentication attempt. For production, increase `maxretry` to reduce false positives and tune `bantime` to suit your environment.
</Callout>

Start/restart fail2ban and check the status:

```bash theme={null}
# Start or restart using systemd
sudo systemctl restart fail2ban
sudo systemctl enable --now fail2ban

# Or reload filters/config without restarting service
sudo fail2ban-client reload

# Show global status
sudo fail2ban-client status

# Show status for the nginx-http-auth jail
sudo fail2ban-client status nginx-http-auth
```

Sample output for the `nginx-http-auth` jail:

```text theme={null}
Status for the jail: nginx-http-auth
|- Filter
|  |- Currently failed: 0
|  |- Total failed:  2
|  `- File list:     /var/log/nginx/error.log
`- Actions
   |- Currently banned: 1
   |- Total banned:     2
   `- Banned IP list:   174.0.252.84
```

To unban an IP:

```bash theme={null}
# Unban an IP from a specific jail
sudo fail2ban-client set nginx-http-auth unbanip 174.0.252.84
```

***

When a client accesses `/admin` from a browser and submits incorrect credentials, the browser's sign-in prompt appears. Repeated failed attempts will be detected by fail2ban and the IP will be banned according to your jail settings.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/5f0mE-FaFIAKk82W/images/Nginx-For-Beginners/Security/Demo-Blocking-Traffic/example-com-signin-admin-masked-password.jpg?fit=max&auto=format&n=5f0mE-FaFIAKk82W&q=85&s=1945b2a7f60db118819c5f5510fc801f" alt="A browser screenshot showing a sign-in dialog for &#x22;https://example.com&#x22; with the username &#x22;admin&#x22; filled in and a masked password field, plus &#x22;Cancel&#x22; and &#x22;Sign In&#x22; buttons. The rest of the page is mostly blank." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Security/Demo-Blocking-Traffic/example-com-signin-admin-masked-password.jpg" />
</Frame>

After a failed attempt (or the number specified by `maxretry`), fail2ban will add the client's IP to the banned list and access to `/admin` (and possibly other HTTP(S) ports) will be blocked until the ban expires or is removed.

***

## Option: Let fail2ban handle blocking instead of large `deny` lists

If you want Nginx config to remain simple and prefer dynamic blocking, do not add `allow` / `deny` rules on the site root. Instead, rely on fail2ban jails to block offenders. Example server config without `allow`/`deny`:

```nginx theme={null}
server {
    listen 443 ssl;

    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/certs/example.com-key.pem;

    root /var/www/html;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";
    add_header Referrer-Policy origin;

    index index.html index.htm index.nginx-debian.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location /admin {
        auth_basic "Restricted Access";
        auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
    }
}
```

This approach keeps Nginx configuration manageable and lets fail2ban dynamically add IP-level blocks in the firewall when repeated failures are detected.

***

## Summary & best practices

* Use Nginx `allow`/`deny` for small, static lists of trusted or blocked IPs.
* Use CIDR ranges (`/24`, `/16`, etc.) to cover networks instead of many `/32` entries.
* For automated handling of attackers (e.g., repeated failed HTTP Basic auth attempts), use [fail2ban](https://www.fail2ban.org/) to monitor Nginx logs and apply temporary bans.
* Tune fail2ban `maxretry` and `bantime` to balance security and the risk of false positives in your environment.

Further reading:

* [Nginx documentation — ngx\_http\_access\_module](https://nginx.org/en/docs/http/ngx_http_access_module.html)
* [fail2ban documentation](https://www.fail2ban.org/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/8905470e-b1ea-48ec-b0cd-711687ce7159/lesson/a317f900-cb06-48ac-822a-12a9f64d432f" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/8905470e-b1ea-48ec-b0cd-711687ce7159/lesson/66fef843-b5b8-4c77-a01b-ce3999f9643c" />
</CardGroup>
