sudo fail2ban-client status nginx-botsearchwhy-banned <ip_address_in_question>If you run fail2ban in front of a public-facing service, you'll eventually want to answer questions like "why was this IP banned?", "what did they try?", or "which scanners are hitting me hardest this week?". The good news: fail2ban already logs everything you need. You just have to know how to ask.
This post collects a handful of one-liners I keep coming back to when investigating ban activity on an nginx server. They assume a setup with fail2ban tailing nginx access logs, but the patterns work for any jail.
198.51.100.0/24 (TEST-NET-2)&& 203.0.113.0/24 (TEST-NET-3) which is an RFC 5737 documentation address (reserved, can never be a real server IP — same idea as 192.168.x.x but for examples).The basic workflow
When you see a banned IP and want to know what it did, you need two pieces of information:
- fail2ban's view: when did the filter match, and when did the ban fire?
- nginx's view: what requests did that IP actually make?
fail2ban writes its activity to /var/log/fail2ban.log. Each match gets a Found line with a timestamp, and each ban gets a Ban line. Cross-referencing those timestamps against your access log tells you exactly which requests triggered the action.
Show recent bans with reasons
The simplest query: what's been happening lately?
sudo grep -E "Ban |Found " /var/log/fail2ban.log | tail -20This shows the last 20 events across all jails. You'll see lines like:
[nginx-botsearch] Found 198.51.100.42 - 2026-05-20 15:07:04
[nginx-botsearch] Ban 198.51.100.42The Found line is the filter matching a log entry. The Ban line is fail2ban actually taking action (which happens once maxretry is exceeded within findtime).
Show all activity for a specific IP
Once you have an IP of interest, pull everything related to it from both logs:
sudo grep "198.51.100.42" /var/log/fail2ban.log /var/log/nginx-proxy/access.log*You'll get the full story: each fail2ban event, plus every request that IP made (across rotated access logs too, since the * glob picks up access.log.1, etc.).
For compressed rotated logs, add a zgrep pass:
sudo zgrep "198.51.100.42" /var/log/nginx-proxy/access.log.*.gzCount bans by IP
Want to know which scanners are the most persistent? Aggregate by IP and sort by frequency:
sudo grep "Ban " /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20Output looks like:
4 198.51.100.42
3 198.51.100.43
2 203.0.113.47
1 203.0.113.182The number on the left is how many times that IP has been banned. Repeat offenders show up at the top — useful if you want to consider a longer ban time for chronic abusers (or feed them into a recidive jail).
Currently banned IPs across all jails
fail2ban exposes a one-shot summary of who's currently in the doghouse:
sudo fail2ban-client bannedThis shows banned IPs grouped by jail. Much faster than grepping the log when you just want to know "is this IP currently blocked?"
For per-jail detail (including filter stats and recent ban history):
sudo fail2ban-client status nginx-botsearchQuality-of-life: a "why-banned" script
If you find yourself running the cross-reference query often, drop it in a script:
sudo tee /usr/local/bin/why-banned > /dev/null <<'EOF'
#!/bin/bash
# Usage: why-banned <ip>
ip="$1"
if [ -z "$ip" ]; then
echo "Usage: why-banned <ip>"
exit 1
fi
echo "=== fail2ban events ==="
sudo grep "$ip" /var/log/fail2ban.log
echo
echo "=== nginx access entries ==="
sudo grep "$ip" /var/log/nginx-proxy/access.log* 2>/dev/null
sudo zgrep "$ip" /var/log/nginx-proxy/access.log.*.gz 2>/dev/null
EOF
sudo chmod +x /usr/local/bin/why-bannedThen any time you see a banned IP and wonder what happened:
why-banned 198.51.100.42You get the full story in one shot: every fail2ban event for that IP, plus every request it made across both live and rotated logs.
Aliases worth saving
For interactive use, add these to ~/.bashrc or ~/.zshrc:
alias f2b-recent='sudo grep -E "Ban |Found " /var/log/fail2ban.log | tail -20'
alias f2b-top='sudo grep "Ban " /var/log/fail2ban.log | awk "{print \$NF}" | sort | uniq -c | sort -rn | head -20'
alias f2b-banned='sudo fail2ban-client banned'Adjust paths to your setup
The commands above assume:
- fail2ban logs to
/var/log/fail2ban.log(the default on Debian/Ubuntu) - nginx access logs are at
/var/log/nginx-proxy/access.logwith rotated copies in the same directory
If your paths differ (e.g., /var/log/nginx/access.log for a non-containerized nginx), substitute accordingly. The patterns themselves don't change.
Why this matters
Bans aren't fire-and-forget. Periodically reviewing what's getting banned helps you spot:
- New attack patterns that your current filters might be missing
- False positives hitting legitimate users or crawlers
- Persistent threats worth escalating to a longer ban time or a permanent block
- Filter misconfiguration — if you see zero bans over a week and you know scanners are hitting you, something's broken
The data has been there the whole time. These queries just make it easy to surface.