Every server with a public IP address is scanned within minutes of going online. Scanners look for open ports, identify the software behind them, and try known vulnerabilities or weak passwords. You can’t stop scans — but you can make sure they find almost nothing worth attacking.
1. See what a scanner sees
From the server, list listening ports:
ss -tulpn
Look at the Local Address column:
127.0.0.1:3306or[::1]:6379— only reachable from the server itself. Good.0.0.0.0:3306or*:6379— reachable from the internet (unless firewalled). Probably not what you want.
From another machine, scan your server to confirm what’s actually exposed:
nmap -Pn -sT --top-ports 1000 YOUR_SERVER_IP
Only scan servers you own.
2. Bind internal services to localhost
Databases, caches and admin interfaces rarely need to be public:
| Service | Setting |
|---|---|
| MariaDB/MySQL | bind-address = 127.0.0.1 |
| PostgreSQL | listen_addresses = 'localhost' |
| Redis | bind 127.0.0.1 ::1 and protected-mode yes |
| Docker containers | publish as -p 127.0.0.1:8080:80, not -p 8080:80 |
Docker deserves special attention: published ports bypass UFW rules because Docker writes its own iptables rules. Binding to 127.0.0.1 and using a reverse proxy is the reliable fix.
3. Default-deny firewall
Allow only what you need:
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
Restrict admin ports to your own IP where possible:
ufw allow from 203.0.113.50 to any port 22 proto tcp
More advanced rules (nftables, rate limits): Linux firewall rules and custom firewall rules.
4. Harden SSH
SSH is the most scanned service on Linux servers.
- Use key authentication and disable passwords.
- Disable direct root login.
- Optionally move SSH to a non-standard port — it doesn’t add real security, but cuts log noise from automated scans.
- Even better: only allow SSH through a VPN (see WireGuard on a VPS).
Details: SSH security on Debian.
5. Ban repeat offenders with Fail2ban
apt install -y fail2ban
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
EOF
systemctl restart fail2ban
fail2ban-client status sshd
Fail2ban also has filters for Nginx, Postfix and more. For scan detection specifically, you can log dropped packets and ban IPs that hit many closed ports — but default-deny already makes those probes harmless.
6. Rate-limit new connections
nftables can limit how fast a single IP opens new connections:
nft add rule inet filter input tcp dport 22 ct state new limit rate over 10/minute drop
(Place it in a proper ruleset; see the firewall guides above.)
7. Hide version information
Reduce what banners reveal:
# nginx.conf
server_tokens off;
; php.ini
expose_php = Off
8. Keep software updated
Scanners matter most when they find a vulnerable version. Automatic security updates close most of those holes — see unattended upgrades.
9. Watch the logs
journalctl -u ssh --since today | grep -c 'Failed'
ufw status verbose
A sudden change in volume can signal a targeted attack rather than background noise.
Summary
Expose only the ports you need, bind everything else to localhost, firewall by default, protect SSH, and keep software patched. Scans then find a server with two or three well-defended doors. More in encrypting VPS traffic and secure web hosting on Linux.