Personal Website Security Protection

A while ago, I mentioned that after connecting log monitoring to my website, I discovered that someone was maliciously scanning it every day, resulting in a lot of 404 access records. The access logs not only waste bandwidth but also hinder my troubleshooting. The online world is treacherous; there are always malicious individuals trying to sabotage me.

Previously, I simply deployed fail2ban to prevent SSH brute-force attacks, but unfortunately, I also added log analysis.

Similarly, I can catch more than 10 malicious IPs every day.

1root@tokyo:~# cat /etc/fail2ban/filter.d/caddy-json-scan.conf
2[Definition]
3# Match requests with status=404 and URIs ending in .php, .cgi, .pl, etc.
4# Extract the IP from remote_addr (automatically ignore ports)
5failregex = ^.*"remote_ip":"<HOST>".*"uri":"[^"]*\.(?i:php|cgi|pl|asp)[^"]*".*"status":404.*
6ignoreregex =

This regular expression approach doesn't work for scanning compressed files. For example, many attacks scan .zip/.tar/.gz, etc., HEAD requests, making it tedious to rewrite the matching rules.

Furthermore, fail2ban consumes a lot of memory and has slow hit rate testing, so an alternative solution needs to be found.

I did some research and found that CrowdSec can handle this task. It's an open-source, community-collaboration-based cybersecurity tool used to detect and block malicious activities (such as brute-force attacks, scanning, and web attacks). It identifies suspicious IP addresses in real-time by analyzing log files and can share threat intelligence with users worldwide (with their consent), creating a "collective immunity" defense network. It also handles Caddy log analysis and SSH/FTP brute-force attack defense with ease, making it seem very suitable for personal use.

1. CrowdSec

Installation and usage are very simple:

1curl -s https://install.crowdsec.net | sudo bash
2sudo apt update
3sudo apt list --upgradable
4sudo apt install crowdsec -y
5sudo apt install crowdsec-firewall-bouncer-nftables -y

The crowdsec-firewall-bouncer-nftables is a bouncer (interceptor) provided by CrowdSec. It automatically adds malicious IP addresses detected by CrowdSec to the Linux nftables firewall rules, thereby achieving real-time blocking at the network layer.

During the installation process, CrowdSec automatically identifies which services your system has (such as Postgres, sshd, Nginx, etc.) and automatically loads the corresponding protection modules, which is much more convenient than manually writing regular expressions for fail2ban.

In other words, once a malicious IP is detected, its access request packets are dropped by the system and never reach the Caddy service, significantly reducing malicious scan records. Bouncers can also be implemented at the application level; for example, Caddy has a corresponding interceptor: https://github.com/hslatman/caddy-crowdsec-bouncer. This means interception at the web server level, allowing for more customized operations compared to system-level interception.

I particularly dislike these malicious scans; they're simply dropped at the system level. My hosting provider pays me on time every month; running a website without making money, trying to serve the public, and then getting attackedโ€”nobody's happy about that.

See the output below and feel the evil of this world:

 1root@tokyo:~#  cscli decisions list
 2โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
 3โ”‚   ID   โ”‚  Source  โ”‚     Scope:Value    โ”‚               Reason              โ”‚ Action โ”‚ Country โ”‚                      AS                     โ”‚ Events โ”‚ expiration โ”‚ Alert ID โ”‚
 4โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
 5โ”‚ 300360 โ”‚ crowdsec โ”‚ Ip:101.47.161.84   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 28     โ”‚ 3h53m54s   โ”‚ 549      โ”‚
 6โ”‚ 300359 โ”‚ crowdsec โ”‚ Ip:101.47.162.19   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 27     โ”‚ 3h53m22s   โ”‚ 548      โ”‚
 7โ”‚ 300358 โ”‚ crowdsec โ”‚ Ip:221.14.65.92    โ”‚ crowdsecurity/http-probing        โ”‚ ban    โ”‚ CN      โ”‚ 4837 CHINA UNICOM China169 Backbone         โ”‚ 12     โ”‚ 3h49m40s   โ”‚ 546      โ”‚
 8โ”‚ 300357 โ”‚ crowdsec โ”‚ Ip:122.192.133.100 โ”‚ crowdsecurity/http-bad-user-agent โ”‚ ban    โ”‚ CN      โ”‚ 4837 CHINA UNICOM China169 Backbone         โ”‚ 2      โ”‚ 3h45m54s   โ”‚ 545      โ”‚
 9โ”‚ 300356 โ”‚ crowdsec โ”‚ Ip:198.98.57.141   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ US      โ”‚ 53667 PONYNET                               โ”‚ 14     โ”‚ 3h35m44s   โ”‚ 543      โ”‚
10โ”‚ 285355 โ”‚ crowdsec โ”‚ Ip:45.78.194.178   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 31     โ”‚ 3h3m16s    โ”‚ 538      โ”‚
11โ”‚ 285354 โ”‚ crowdsec โ”‚ Ip:92.118.39.76    โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ US      โ”‚ 47890 Unmanaged Ltd                         โ”‚ 19     โ”‚ 3h2m50s    โ”‚ 537      โ”‚
12โ”‚ 285353 โ”‚ crowdsec โ”‚ Ip:45.78.219.42    โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 24     โ”‚ 2h53m55s   โ”‚ 535      โ”‚
13โ”‚ 285352 โ”‚ crowdsec โ”‚ Ip:45.78.229.72    โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 16     โ”‚ 2h45m33s   โ”‚ 534      โ”‚
14โ”‚ 285351 โ”‚ crowdsec โ”‚ Ip:173.249.50.59   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ FR      โ”‚ 51167 Contabo GmbH                          โ”‚ 15     โ”‚ 2h43m43s   โ”‚ 533      โ”‚
15โ”‚ 285350 โ”‚ crowdsec โ”‚ Ip:159.89.143.86   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ US      โ”‚ 14061 DIGITALOCEAN-ASN                      โ”‚ 15     โ”‚ 2h43m12s   โ”‚ 532      โ”‚
16โ”‚ 285349 โ”‚ crowdsec โ”‚ Ip:23.236.169.182  โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ US      โ”‚ 55286 SERVER-MANIA                          โ”‚ 13     โ”‚ 2h40m12s   โ”‚ 531      โ”‚
17โ”‚ 285348 โ”‚ crowdsec โ”‚ Ip:103.175.206.22  โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ ID      โ”‚ 147124 PT Hostingan Awan Indonesia          โ”‚ 13     โ”‚ 2h40m0s    โ”‚ 530      โ”‚
18โ”‚ 285347 โ”‚ crowdsec โ”‚ Ip:163.61.198.41   โ”‚ crowdsecurity/http-probing        โ”‚ ban    โ”‚ SG      โ”‚ 9664 SOUTHEAST ASIA TELECOMSG PTE. LTD.     โ”‚ 13     โ”‚ 2h37m41s   โ”‚ 529      โ”‚
19โ”‚ 285346 โ”‚ crowdsec โ”‚ Ip:45.78.193.199   โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 27     โ”‚ 2h34m44s   โ”‚ 528      โ”‚
20โ”‚ 285345 โ”‚ crowdsec โ”‚ Ip:101.47.163.243  โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ SG      โ”‚ 150436 Byteplus Pte. Ltd.                   โ”‚ 21     โ”‚ 2h30m40s   โ”‚ 527      โ”‚
21โ”‚ 285344 โ”‚ crowdsec โ”‚ Ip:162.240.109.153 โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ US      โ”‚ 46606 UNIFIEDLAYER-AS-1                     โ”‚ 14     โ”‚ 2h22m55s   โ”‚ 524      โ”‚
22โ”‚ 285343 โ”‚ crowdsec โ”‚ Ip:181.167.144.229 โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ AR      โ”‚ 7303 Telecom Argentina S.A.                 โ”‚ 18     โ”‚ 2h22m28s   โ”‚ 523      โ”‚
23โ”‚ 285342 โ”‚ crowdsec โ”‚ Ip:95.90.13.168    โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ DE      โ”‚ 3209 Vodafone GmbH                          โ”‚ 12     โ”‚ 2h21m59s   โ”‚ 522      โ”‚
24โ”‚ 285341 โ”‚ crowdsec โ”‚ Ip:211.106.133.202 โ”‚ crowdsecurity/ssh-slow-bf         โ”‚ ban    โ”‚ KR      โ”‚ 4766 Korea Telecom                          โ”‚ 14     โ”‚ 2h21m32s   โ”‚ 521      โ”‚
25โ”‚ 285340 โ”‚ crowdsec โ”‚ Ip:69.63.203.162   โ”‚ crowdsecurity/http-probing        โ”‚ ban    โ”‚ US      โ”‚ 3257 GTT Communications Inc.                โ”‚ 11     โ”‚ 2h9m41s    โ”‚ 519      โ”‚
26โ”‚ 285339 โ”‚ crowdsec โ”‚ Ip:49.86.41.49     โ”‚ crowdsecurity/http-bad-user-agent โ”‚ ban    โ”‚ CN      โ”‚ 146966 China Telecom                        โ”‚ 2      โ”‚ 1h54m39s   โ”‚ 518      โ”‚
27โ”‚ 285338 โ”‚ crowdsec โ”‚ Ip:43.134.66.41    โ”‚ crowdsecurity/http-probing        โ”‚ ban    โ”‚ SG      โ”‚ 132203 Tencent Building, Kejizhongyi Avenue โ”‚ 11     โ”‚ 1h29m46s   โ”‚ 515      โ”‚
28โ”‚ 270337 โ”‚ crowdsec โ”‚ Ip:20.78.169.245   โ”‚ crowdsecurity/http-wordpress-scan โ”‚ ban    โ”‚ JP      โ”‚ 8075 MICROSOFT-CORP-MSN-AS-BLOCK            โ”‚ 4      โ”‚ 56m13s     โ”‚ 513      โ”‚
29โ”‚ 270334 โ”‚ crowdsec โ”‚ Ip:152.42.225.101  โ”‚ crowdsecurity/http-probing        โ”‚ ban    โ”‚ SG      โ”‚ 14061 DIGITALOCEAN-ASN                      โ”‚ 11     โ”‚ 21m40s     โ”‚ 508      โ”‚
30โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

CrowdSec has a wealth of small features, which I won't list here. Overall, it's suitable for personal websites and worth recommending.

2. Caddy Integration with Coraza WAF and rate_limit

Larger companies typically have a WAF layer on the outermost layer of their services; rate_limit can prevent CC attacks, API breaches, brute-force attacks, etc., so we should also include it.

1CGO_ENABLED=1 \
2xcaddy build \
3  --with github.com/dunglas/caddy-cbrotli \
4	--with github.com/mholt/caddy-ratelimit \
5	--with github.com/corazawaf/coraza-caddy/v2

I compiled the above non-standard modules on my computer and then uploaded them directly to the server. The server configuration was too low, so it couldn't compile and would affect users. caddy-cbrotli adds br support; the last two are for ratelimit and WAF.

Result Verification:

 1โžœ  ~ ./caddy list-modules | tail -n 10
 2
 3  Standard modules: 127
 4
 5http.encoders.br
 6http.handlers.rate_limit
 7http.handlers.waf
 8
 9  Non-standard modules: 3
10
11  Unknown modules: 0

WAF Configuration Snippet Example:

 1# --- 1. Coraza WAF Core Protection ---
 2# For static sites, we mainly block abnormal scanning and malicious User-Agent
 3coraza_waf {
 4    directives `
 5        SecRuleEngine On
 6        SecRequestBodyAccess On
 7        SecDebugLogLevel 0
 8
 9        # Block common malicious scanners
10        SecRule REQUEST_HEADERS:User-Agent "(sqlmap|grabber|cgiscan|nessus|nikto|dirbuster|pangolin)" \
11            "id:101,phase:1,deny,status:403,msg:'Malicious Scanner Detected'"
12
13        # Prevent attempts to access sensitive file paths
14        SecRule REQUEST_URI "\.(git|env|config|php|asp|aspx|jsp|cgi|exe)$" \
15            "id:102,phase:1,deny,status:404,msg:'Path Traversal Attempt'"
16    `
17}

Test verification is normal, as shown below:

1โžœ ~ curl -A "sqlmap" https://mephisto.cc
2Error: 403 ForbiddenโŽ
3โžœ ~ curl -I https://mephisto.cc/.env
4HTTP/2 404
5alt-svc: h3=":443"; ma=2592000

Rate Limiting Example:

 1:80
 2
 3rate_limit {
 4	distributed
 5	zone static_example {
 6		match {
 7			method GET
 8		}
 9		key    static
10		events 100
11		window 1m
12	}
13	zone dynamic_example {
14		key    {remote_host}
15		events 2
16		window 5s
17	}
18	log_key
19}
20
21respond "I'm behind the rate limiter!"

I added rate limiting a long time ago because I was really worried about people spamming the API.

Some RSS software not only accesses the RSS source file but also frequently scrapes the full text, which defies common sense. I also dealt with these through UA identification.

3. Summary

This combination of measures can basically defend against some common attacks for personal servers without any additional cost. Small websites operate with integrity and low profile, serving the people. Since there's no profit to be made, it's probably not a target for professional attack groups.

Lastmod๏ผš Saturday, January 31, 2026

See Also:

Translations: