Block known malicious IPs at the Nginx level before they reach your application. A cron job pulls the latest threat data from SikkerAPI's Blacklist API and writes it to a file that Nginx reads on reload.
This works for standalone Nginx web servers, reverse proxies, and CDN edge nodes. No Lua modules or OpenResty required — it uses Nginx's built-in geo module. See our Blacklist API docs for the full parameter reference, or check pricing for tier limits.
Cron runs every N hours
|
v
Fetches blacklist from SikkerAPI
|
v
Writes Nginx geo map file
|
v
Reloads Nginx (nginx -s reload)
|
v
Blocked IPs get 403 ForbiddenDownload the script that fetches the plaintext blacklist from SikkerAPI and converts it into an Nginx geo map file. It only reloads Nginx if the list has actually changed.
Save your API key and set the correct permissions. Run it once to generate the initial blocklist file.
Add the geo block to your nginx.conf (inside the http context) and use it in your server blocks to deny blocked IPs.
Run the update script on a schedule that fits your tier's blacklist quota.
$ sudo curl -o /usr/local/bin/sikkerapi-nginx-update \ "https://sikkerapi.com/nginx/sikkerapi-nginx-update.sh"
$ echo "sk_free_..." | sudo tee /etc/nginx/sikkerapi.key $ sudo chmod 600 /etc/nginx/sikkerapi.key $ sudo chmod +x /usr/local/bin/sikkerapi-nginx-update
$ sudo /usr/local/bin/sikkerapi-nginx-update SikkerAPI: blocklist updated (4832 IPs), Nginx reloaded
http {
# Load SikkerAPI blocklist
geo $sikkerapi_blocked {
default 0;
include /etc/nginx/sikkerapi_blocklist.conf;
}
server {
# Block matched IPs
if ($sikkerapi_blocked) {
return 403;
}
# ... rest of your config
}
}0 */12 * * * /usr/local/bin/sikkerapi-nginx-update.sh >> /var/log/sikkerapi-nginx.log 2>&1
If Nginx sits behind a load balancer, CDN, or another proxy, the client IP is in the X-Forwarded-For header, not $remote_addr. Use the realip module to extract the real client IP before the geo check runs.
For CDN edge nodes specifically: block at the edge to prevent malicious traffic from reaching upstream servers. This stops your upstream from seeing the bad IPs and potentially blocking your CDN nodes.
If your Nginx only serves HTTP/HTTPS, add protocols=http to your API call to only fetch IPs with observed HTTP-layer attack activity. This gives you a more targeted list for web-specific threats.
http {
# Trust your upstream proxy/LB
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Now geo uses the real client IP
geo $sikkerapi_blocked {
default 0;
include /etc/nginx/sikkerapi_blocklist.conf;
}
}$ curl -sf \ -H "Authorization: Bearer $API_KEY" \ "https://api.sikkerapi.com/v1/key/blacklist?plaintext=true&scoreMinimum=70&protocols=http"
Customize the blacklist query to match your use case. The most important parameter is scoreMinimum — the confidence threshold for including an IP.
| Score | Suggested Use |
|---|---|
| 90+ | Very high confidence. Suitable for hard-blocking in most environments. Always review your logs periodically for false positives. |
| 70-89 | High confidence. Good default for most setups. Review your logs periodically for false positives. |
| 50-69 | Moderate confidence. Consider rate limiting or challenging instead of hard-blocking. |
All Blacklist API filters are available: country, ASN, protocol, IP version, and severity. See the full parameter reference for details.
$ curl -sf \ -H "Authorization: Bearer $API_KEY" \ "...?plaintext=true&scoreMinimum=90"
$ curl -sf \ -H "Authorization: Bearer $API_KEY" \ "...?plaintext=true&scoreMinimum=70&exceptCountries=US,GB,DE"
$ curl -sf \ -H "Authorization: Bearer $API_KEY" \ "...?plaintext=true&scoreMinimum=70&ipVersion=4&limit=5000"
Instead of a single blocklist, use two lists at different confidence thresholds: hard-block the high-confidence threats and challenge the medium-confidence ones with a captcha or interstitial page.
This approach reduces false positives for borderline IPs while still protecting against confirmed threats. The captcha page is served by your application — SikkerAPI provides the signal, you decide the challenge.
The challenge list uses the same update script with different parameters. On solving the captcha, your app sets a cookie that bypasses the check.
#!/bin/bash set -euo pipefail API_KEY="sk_free_..." API_URL="https://api.sikkerapi.com/v1/key/blacklist" # High confidence - hard block curl -sf \ -H "Authorization: Bearer $API_KEY" \ "$API_URL?plaintext=true&scoreMinimum=90" \ | awk '{print $1" 1;"}' \ > /etc/nginx/sikkerapi_block.conf # Medium confidence - challenge curl -sf \ -H "Authorization: Bearer $API_KEY" \ "$API_URL?plaintext=true&scoreMinimum=50&limit=5000" \ | awk '{print $1" 1;"}' \ > /etc/nginx/sikkerapi_challenge.conf nginx -t && nginx -s reload
geo $sikkerapi_block {
default 0;
include /etc/nginx/sikkerapi_block.conf;
}
geo $sikkerapi_challenge {
default 0;
include /etc/nginx/sikkerapi_challenge.conf;
}
server {
# Hard block high-confidence threats
if ($sikkerapi_block) {
return 403;
}
# Challenge medium-confidence (skip if cookie set)
set $needs_challenge 0;
if ($sikkerapi_challenge) {
set $needs_challenge 1;
}
if ($cookie_sikker_verified) {
set $needs_challenge 0;
}
if ($needs_challenge) {
return 302 /challenge;
}
}Each IP returned from the blacklist endpoint counts against your daily blacklist quota (separate from your lookup quota). Plan your refresh frequency around your tier's limits.
| Tier | Blacklist IPs/Day |
|---|---|
| Free | 5,000 |
| Basic | 50,000 |
| Small Business | 75,000 |
| Medium Business | 150,000 |
| Large Business | 350,000 |
Pull your full tier limit once per day. The blacklist is sorted by confidence score (highest first), then by recency — so you always get the most dangerous IPs with the most recently active ones prioritized among equal scores. One daily pull gives you maximum coverage without wasting quota.
| Example | Cron |
|---|---|
| Once daily at 3 AM | 0 3 * * * |
| Once daily at midnight UTC | 0 0 * * * |
Check the response headers X-Blacklist-Remaining to monitor your usage.
$ curl -sI \ -H "Authorization: Bearer $API_KEY" \ "https://api.sikkerapi.com/v1/key/blacklist?plaintext=true&limit=1" \ | grep -i x-blacklist X-Blacklist-Limit: 10000 X-Blacklist-Used: 5000 X-Blacklist-Remaining: 5000
$ wc -l /etc/nginx/sikkerapi_blocklist.conf 4832 /etc/nginx/sikkerapi_blocklist.conf $ head -5 /etc/nginx/sikkerapi_blocklist.conf # SikkerAPI blocklist - Wed Feb 19 22:00:00 UTC 2026 # IPs: 4832 203.0.113.42 1; 198.51.100.17 1; 192.0.2.99 1;
$ nginx -t nginx: configuration file /etc/nginx/nginx.conf test is successful
$ curl -s -o /dev/null -w "%{'http_code'}" \ --resolve example.com:80:203.0.113.42 \ http://example.com/ 403
$ tail -f /var/log/nginx/access.log \
| grep " 403 "