When implementing IP blacklisting in Nginx, simply including a deny
directive in your main configuration might not work as expected. The key problem lies in Nginx's configuration hierarchy and context sensitivity.
For blacklisting to work effectively, you need to place the rules in the server context (virtual host configuration) rather than just the http context. Here's why:
# Correct placement example in site configuration
server {
listen 80;
server_name example.com;
# IP blacklist inclusion
include /etc/nginx/conf.d/blockips.conf;
# Rest of your server config...
}
Several factors could make your blacklist ineffective:
- Inclusion at wrong configuration level (http vs server context)
- Missing reload/restart after configuration changes
- Conflict with other access control rules
- Proxy or load balancer obscuring real client IPs
For comprehensive protection, consider this enhanced approach:
# /etc/nginx/conf.d/blockips.conf
deny 58.218.199.250;
deny 123.45.67.0/24;
deny 192.168.1.100;
Then in each virtual host:
server {
# ... other directives ...
include /etc/nginx/conf.d/blockips.conf;
location / {
# Blacklist applies to all locations by default
try_files $uri $uri/ =404;
}
}
After making changes:
- Test configuration:
nginx -t
- Reload Nginx:
systemctl reload nginx
orservice nginx reload
- Verify using:
curl -I http://yourdomain.com -H "Host: example.com" -x IP_ADDRESS:80
For dynamic blacklisting, consider using Nginx's ngx_http_geo_module
:
http {
geo $blocked_ip {
default 0;
58.218.199.250 1;
123.45.67.89 1;
}
server {
if ($blocked_ip) {
return 403;
}
}
}
When implementing IP blacklisting in Nginx, many administrators encounter situations where blocked IPs still appear in access logs. This typically happens due to incorrect configuration placement or missing context-specific directives.
The current configuration in nginx.conf
includes the blacklist at the http context level:
http {
# Other configurations...
include /etc/nginx/conf.d/blockips.conf;
}
While syntactically correct, this placement may not achieve the desired blocking effect because:
- HTTP context directives don't automatically apply to server or location contexts
- Some requests might bypass these rules if processed differently
For effective IP blocking, you should include the blacklist in each server context. Here's the proper way to structure it:
server {
listen 80;
server_name example.com;
include /etc/nginx/conf.d/blockips.conf;
location / {
# Your regular configuration
}
}
Consider using these additional techniques for more robust blocking:
# Basic IP deny
deny 58.218.199.250;
# CIDR block
deny 123.45.67.0/24;
# Country blocking (requires GeoIP module)
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default yes;
CN no;
RU no;
}
After making changes, always:
- Test configuration syntax:
nginx -t
- Reload Nginx:
systemctl reload nginx
- Verify with test requests:
curl -I http://yoursite.com
For more sophisticated protection, consider implementing fail2ban or writing a script to dynamically update your blocklist:
#!/bin/bash
# Simple script to update Nginx blocklist
BAD_IPS=$(grep "BadBot" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq)
echo "# Auto-generated at $(date)" > /etc/nginx/conf.d/blockips.conf
for ip in $BAD_IPS; do
echo "deny $ip;" >> /etc/nginx/conf.d/blockips.conf
done
nginx -s reload
- Forgetting to reload Nginx after configuration changes
- Placing deny rules after allow rules (order matters)
- Not accounting for reverse proxy setups where real IP is in X-Forwarded-For
Use this command to verify blocked requests:
grep ' 403 ' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -nr