DEV Community

Lenny
Lenny

Posted on

caddy-analyzer: The security-first log analyzer Caddy has been missing

If you're running Caddy v2 in production, you've probably noticed something: the standard log analysis tools don't work. GoAccess, lnav, even grep — they all choke on Caddy's nested JSON schema. You end up writing brittle one-off scripts to parse TLS versions, nested request headers, and float durations.

I got tired of that. So I built caddy-analyzer.

What is it?

caddy-analyzer is a CLI tool (written in Go) that natively parses Caddy v2 structured JSON logs and layers a full security detection engine on top. Think of it as goaccess + fail2ban + threat intelligence, purpose-built for Caddy.

The problem with Caddy logs

Caddy doesn't use Common/Combined Log Format. It outputs structured JSON like this:

{
  "request": {
    "method": "GET",
    "uri": "/search?id=1' OR '1'='1",
    "remote_ip": "192.0.2.42",
    "proto": "HTTP/2.0"
  },
  "http": {
    "status_code": 403,
    "duration": 0.0023
  },
  "tls": {
    "version": "tls1.3",
    "cipher_suite": "TLS_AES_256_GCM_SHA384"
  }
}
Enter fullscreen mode Exit fullscreen mode

Existing tools expect flat log lines. They can't handle nested maps, so you're stuck writing custom parsers. And more importantly: none of them detect attacks.

What caddy-analyzer does

Security detection — 26 categories

Every request is scanned against 26 attack categories using a dual-pass engine (URL-unescaped + raw URI matching) to catch multibyte and double-encoded bypass attempts:

Category What it catches
SQL Injection UNION SELECT, blind injection, DB fingerprinting
XSS <script>, event handlers, data:text/html
SSRF Cloud metadata (169.254.169.254), loopback IPs, protocol smuggling
RCE Shell injection, reverse shells, eval(), deserialization
Path Traversal / LFI ../, null byte, /etc/passwd, php://input
SSTI Jinja2, Freemarker, ERB, Thymeleaf template injection
Log4j / JNDI ${jndi:ldap://, obfuscated variants
XXE / XInclude XML entity expansion, external DTD
CRLF Injection Header injection, log poisoning
...and 17 more GraphQL, LDAP, XPath, JWT abuse, beaconing/C2, scanner detection

Every detection is tagged with MITRE ATT&CK technique IDs. Results are grouped by offending IP with request details.

Real-time IP blocking

The guard subcommand monitors your log stream and auto-blocks malicious IPs via iptables:

sudo caddy-analyze guard docker://my-caddy
Enter fullscreen mode Exit fullscreen mode

Features:

  • Sliding window rate limiting (per-IP, per-second buckets)
  • 8 default blocklist feeds (Spamhaus, FireHOL, CINS, Tor exit nodes, Emerging Threats, AbuseIPDB)
  • GeoIP country blocking: --country-block CN,RU,IR
  • Audit logging and state persistence across restarts
  • Subnet limiting for distributed scan defense

Live threat highlighting

Stream logs in real-time with inline threat detection:

caddy-analyze tail --detect docker://my-caddy
Enter fullscreen mode Exit fullscreen mode
21:06:07  404 WARN  GET /cms/gather/getArticle  (1.71 KB, 2.27ms) - 2.58.137.2 [macOS/Safari] → XSS · RCE
21:06:07  404 WARN  GET /wp-content/plugins/restropress/readme.txt  (9 B, 73µs) - 2.58.137.2 [Linux/Firefox] → WP
21:06:06  200 OK  GET /  (3.04 KB, 5.95ms) - 2.58.137.2 [macOS/Firefox]
Enter fullscreen mode Exit fullscreen mode

IPs are colored by severity (red = critical, amber = medium, olive = low). Clean entries show no markers — zero visual noise.

Everything else

  • TUI dashboard (--watch) — 8-tab live interface with security alerts, GeoIP, operational events
  • HTML reports — standalone dark-mode reports for sharing with your team
  • Diff engine — compare two log files to detect 5xx spikes and latency regressions
  • GeoIP enrichment — offline mmdb lookup, auto-download, no API key needed
  • Sigma export — export detection rules as Sigma YAML for SIEM import
  • Multi-source — files, Docker (docker://), K8s (k8s://), journalctl (journalctl://)
  • Self-update with cosign signature verification

Quick start

# Install (Linux/macOS)
curl -sSfL https://raw.githubusercontent.com/lenny-ts/caddy-analyzer/main/install.sh | bash

# Set your Caddy log path
caddy-analyze config /var/log/caddy/access.log

# Full security scan
caddy-analyze --detect

# Real-time streaming with detection
caddy-analyze tail --detect docker://my-caddy

# Launch interactive dashboard
caddy-analyze --watch
Enter fullscreen mode Exit fullscreen mode

Or try it with the included sample logs (no live Caddy needed):

git clone https://github.com/lenny-ts/caddy-analyzer.git
cd caddy-analyzer
caddy-analyze --detect testdata/sample.log
Enter fullscreen mode Exit fullscreen mode

Why not just use goaccess/lnav/grep?

Capability caddy-analyzer goaccess lnav grep/awk
Caddy v2 JSON native
Security threat detection (26 categories)
Real-time firewall guard (iptables)
Dual-pass evasion-resistant detection
Comparative diff engine
TUI dashboard
Multi-source (Docker, K8s, journalctl)
GeoIP enrichment (offline)

Performance

Tested on a single core with synthetic logs (10% attack traffic):

Log size --detect Parse only RAM
1.5K lines (real) 0.6s <0.1s 21 MB
10K lines 1.5s 0.2s 25 MB
100K lines 14.3s 1.4s 53 MB
1M lines 2m29s ~14s 138 MB

~7,000 lines/sec with detection, ~70,000 lines/sec parse-only.

Try it out

The repo ships sample logs with all 26 attack categories using TEST-NET IPs (RFC 5737) — no real hosts involved:

caddy-analyze --detect testdata/sample.log          # curated sample
caddy-analyze --detect testdata/large.log           # 50K lines, ~27MB
caddy-analyze -f html -o report.html --detect testdata/sample.log  # HTML report
Enter fullscreen mode Exit fullscreen mode

Contributing

PRs and issues welcome! See CONTRIBUTING.md.

GitHub: github.com/lenny-ts/caddy-analyzer

If you find it useful, a ⭐ on GitHub would mean a lot. It helps other Caddy users discover the tool.


Written with ❤️ for the Caddy community

Top comments (0)