All commands
This page explains what every command does, what it reads or changes, and which mode to choose. Use caddy-analyze <command> --help for the exact flags installed in your binary. The complete flag section separates persistent flags, root-only modes and command-specific options.
Choose a command
| Goal | Command | Effect |
|---|---|---|
| Summarize one or more logs | caddy-analyze SOURCE... | Read-only report |
| Find suspicious requests | caddy-analyze --detect SOURCE... | Read-only threat report |
| Watch new entries | tail or --follow | Read-only stream/report |
| See rankings | top | Read-only aggregation |
| Compare two periods | diff | Read-only comparison |
| Store a reference period | baseline save | Writes JSON |
| Automatically block attackers | guard | May change firewall rules |
| Block or unblock manually | block / unban | Changes firewall rules |
| Manage threat-intel feeds | blocklist | Writes cache/config, not firewall |
| Manage defaults | config | Writes configuration |
| Maintain protected IPs | whitelist | Writes allowlist |
| Export detection rules | export-sigma | Writes YAML or stdout |
| Install a release | update | Replaces the binary |
Analyze: the root command
caddy-analyze [flags] [source...]
caddy-analyze --detect --format json access.log
caddy-analyze --from 1h --5xx /var/log/caddy/*.log
With no subcommand, the tool parses Caddy JSON access logs and operational events, applies filters, aggregates metrics, and prints a table report. Sources can be local files or globs, - for stdin, docker://container, k8s://pod, or journalctl://unit. If no source is supplied, the configured default source is used.
--detect enables the dual-pass security engine and adds suspicious IPs, categories, confidence and evidence to the report. It does not block anything. Use --format json for automation, --format csv for spreadsheets, and --format html -o report.html for sharing. Filtering flags such as --ip, --path, --slow and --5xx are combined as AND conditions; --status, country and ASN lists match any value in their list.
Use --follow for periodic reports, --interval 5m for windowed aggregation, or --watch for the interactive dashboard. These root-only modes cannot be combined with each other where the CLI reports them as mutually exclusive.
# Pipe logs and produce machine-readable output
cat access.log | caddy-analyze --detect --format json -
# Inspect only slow server errors from a time window
caddy-analyze --from 2h --5xx --slow 500ms access.log
# Send analyzed documents to Loki or Elasticsearch-compatible storage
caddy-analyze --format loki --remote-url https://logs.example/api/prom/push access.log
tail
caddy-analyze tail [source...] [--detect]
Streams entries as they arrive and colorizes status, method, path, latency and client IP. It is the interactive choice for troubleshooting a live service. --detect highlights matching requests inline; it does not modify the firewall. Filters still apply, so a filtered-out entry is not printed.
caddy-analyze tail docker://caddy --detect --5xx
caddy-analyze tail --grep '/admin|wp-login' /var/log/caddy/access.log
top
caddy-analyze top [dimension] [source...] [flags]
Computes only a ranked view instead of a full report. Dimensions are path, ip, ua, status, method, host, bandwidth, country, city and asn. The dimension may be positional or supplied with --by; path is the default. Use -t 0 to disable the top list, and use normal filters to limit the population before ranking.
caddy-analyze top ip access.log -t 20
caddy-analyze top bandwidth access.log --from 24h
caddy-analyze top --by status --5xx access.log
caddy-analyze top country --geoip-db GeoLite2-Country.mmdb access.log
diff
caddy-analyze diff <baseline_log> <target_log> [flags]
Compares two local log files and reports changes in request rate, status errors and latency. The first file is the reference period and the second is the period under investigation. It does not require or create a saved baseline. Use --format json or --output for CI and scheduled checks.
caddy-analyze diff yesterday.log today.log
caddy-analyze diff before.log after.log --format json -o regression.json
baseline save
caddy-analyze baseline save <source...> -o baseline.json [--detect]
Parses a source and stores a versioned JSON snapshot of aggregate metrics. This is useful for keeping a known-good deployment reference. It writes the file specified by --output; use filters to ensure the baseline and later comparisons cover the same population. --detect includes detection statistics. Saving a baseline does not block IPs.
caddy-analyze baseline save access.log --from 7d -o baseline.json --detect
caddy-analyze --against baseline.json access.log --threshold 15
guard
sudo caddy-analyze guard [source] [flags]
Continuously monitors a source and automatically blocks IPs that cross request, authentication, not-found or detection thresholds. Depending on --firewall-backend, it can modify iptables, nftables, Docker's DOCKER-USER chain, or a hybrid of them. Blocks can be persisted in the state file and every action can be audited.
--never-block or --never-block-file before running on a remote host. Use --dry-run first. Normal mode needs root; dry-run does not change firewall or state.sudo caddy-analyze guard /var/log/caddy/access.log --dry-run
sudo caddy-analyze guard docker://caddy --never-block 203.0.113.10 \
--firewall-backend hybrid --state-file /var/lib/caddy-analyzer/blocked.json
--country-block blocks matching countries immediately when GeoIP is available. Blocklist feeds are enabled by default and can be disabled with --no-blocklist. Audit notifications support JSONL, syslog and generic, Slack, Discord or PagerDuty webhooks. See the dedicated guard reference for all thresholds and sinks.
block and unban
sudo caddy-analyze block <ip> [ip...]
sudo caddy-analyze unban <ip> [ip...] | --all | --list
block adds one or more addresses to the firewall and, unless disabled, synchronizes the guard state file. unban removes selected addresses; --list only displays current state and --all removes every tracked block. Configure the same state file and audit sinks used by guard when these commands are part of an incident workflow.
sudo caddy-analyze block 198.51.100.7 --webhook-url https://hooks.example/...
sudo caddy-analyze unban --list
sudo caddy-analyze unban 198.51.100.7
blocklist
caddy-analyze blocklist refresh|list|config|init [flags]
Downloads and parses threat-intelligence feeds used by guard. refresh updates the cache, list shows active feeds and counts, config prints effective configuration, and init persists CLI settings. These operations do not install firewall rules.
caddy-analyze blocklist refresh
caddy-analyze blocklist list -f json
caddy-analyze blocklist --blocklist-config custom.json \
--no-default-blocklists init
Use --cache-dir for a service-owned cache. Custom JSON sources use name, url and format; validate access and format before enabling them in guard.
config
caddy-analyze config [show|set|reset|source] [--global]
Sets the default source used when the root command has no positional source. Without --global, configuration is stored in ./caddy-analyzer.json; with it, the file is stored under ~/.config/caddy-analyzer/config.json. Local configuration is project-specific and takes precedence over the global location. show displays the active choice and reset removes the selected file.
caddy-analyze config /var/log/caddy/access.log
caddy-analyze config docker://caddy --global
caddy-analyze config show
caddy-analyze config reset
whitelist
sudo caddy-analyze whitelist [--add IP/CIDR] [--remove IP/CIDR]
caddy-analyze whitelist --list [--file PATH]
Maintains a file of IPs and CIDRs that guard must never block. The default is /etc/caddy-analyzer/whitelist.txt. --init creates it with a header, --add and --remove accept comma-separated values, and --list prints entries. Point guard at the same file with --never-block-file.
export-sigma
caddy-analyze export-sigma [output-file]
Exports the built-in detection categories as multi-document Sigma YAML. With no file it writes to stdout, which makes it suitable for piping to a validator; with a file it writes the complete rule set. The export is vendor-neutral and includes MITRE ATT&CK tags. Custom runtime patterns are not silently converted unless supported by the exporter.
caddy-analyze export-sigma rules.yml
caddy-analyze export-sigma - | sigma check
update
caddy-analyze update [--check|--version TAG|--force]
Checks for and installs a verified release from GitHub. Installation is fail-closed: cosign must validate the signed release manifest and the archive checksum must match it. --check performs no installation, --version pins a tag, --force permits reinstall or downgrade, and --install-dir selects a directory. If verification fails, the current binary is left untouched.
caddy-analyze update --check
sudo caddy-analyze update
caddy-analyze update --version v0.7.0 --install-dir ~/.local/bin
completion and version
caddy-analyze --version
caddy-analyze completion bash > ~/.local/share/bash-completion/completions/caddy-analyze
caddy-analyze completion zsh
caddy-analyze completion fish
--version prints the installed version. The hidden completion command generates shell completions for bash, zsh or fish; it requires exactly one supported shell name.
Useful combinations
# Investigate a suspected scan without taking action
caddy-analyze --detect --grep 'wp-login|\.env|/admin' access.log
# Create a comparable baseline and check for a regression
caddy-analyze baseline save baseline.log -o baseline.json
caddy-analyze --against baseline.json current.log --threshold 20
# Preview guard decisions, then enable enforcement
sudo caddy-analyze guard access.log --dry-run --never-block-file whitelist.txt
sudo caddy-analyze guard access.log --never-block-file whitelist.txt
Global flags: complete reference
Most options below are persistent and inherited by subcommands. The root-only modes are --follow, --interval, --watch, root --detect and --version; tail has its own local --detect. An omitted value keeps the default. Durations use Go syntax such as 500ms, 5m or 1h.
| Flag | Default | What it does |
|---|---|---|
--from | none | Keeps entries after an RFC3339 time or relative period such as 5m, 1h or 2d. |
--to | none | Keeps entries before the supplied RFC3339 time. |
--status, -s | all | Selects one or more HTTP status codes, for example -s 200,404; repeatable. |
--2xx, --3xx, --4xx, --5xx | false | Shortcuts for selecting a status class. |
--errors-only, -e | false | Keeps only 5xx server errors. |
--method, -m | none | Filters by HTTP method, case-insensitively. |
--path, -p | none | Filters paths with a glob such as /api/*. |
--host | none | Filters by a substring of the request host. |
--ip / --exclude-ip | none | Include or exclude an IP address or CIDR network. |
--country / --exclude-country | none | Include or exclude ISO country codes or names; requires GeoIP. |
--asn / --exclude-asn | none | Include or exclude ASN numbers; requires GeoIP. |
--slow | none | Selects requests slower than the supplied duration. |
--max-latency | none | Selects requests no slower than the supplied duration. |
--min-size / --max-size | none | Bounds response size in bytes; accepts kb, mb and gb suffixes. |
--grep | none | Searches URI, User-Agent and IP using a regex, with substring fallback for invalid patterns. |
--no-bots / --bots-only | false | Exclude or include only traffic classified as bots and crawlers. |
--level | all | Filters operational events by error, warn, info or debug; repeatable. |
--ops-only | false | Shows only non-HTTP operational events. |
--top, -t | 10 | Number of ranked items; 0 disables top lists. |
--format, -f | table | Selects table, json, csv, html, elasticsearch, opensearch or loki where supported. |
--output, -o | stdout | Writes output to a file instead of stdout. |
--compact, -c | false | Uses the compact table layout. |
--defang | false | Replaces dots in IPs with [.] for safe IOC sharing. |
--workers | available CPUs | Sets parser worker count; 0 selects automatically and keeps reduction ordered. |
--max-cardinality | 100000 | Caps distinct keys per counter to bound memory; 0 is unlimited. |
--trust-forwarded | false | Uses the last public X-Forwarded-For/X-Real-IP hop; enable only behind a trusted proxy. |
--geoip-db | auto | Sets a DB-IP or MaxMind mmdb path; empty enables auto-discovery. |
--no-auto-download | false | Prevents the first-use DB-IP database download. |
--geo-cache-ttl | 24h | Sets GeoIP cache lifetime; 0 disables caching. |
--geo-cache-size | 50000 | Sets maximum cached GeoIP lookups; 0 disables caching. |
--ua-rotation | 10 | Distinct User-Agent count from one IP before the rotation/scanner heuristic fires. |
--custom-patterns | none | Loads one or more validated custom detection JSON files. |
--against | none | Compares current analysis with a baseline JSON and applies the threshold. |
--threshold | 20% | Regression percentage used by baseline comparison. |
--namespace, -n | none | Kubernetes namespace for k8s:// sources. |
--follow, -F | false | Follows new logs and emits periodic reports; root mode. |
--interval, -i | none | Sets aggregation window length in follow mode. |
--watch, -w | false | Opens the live TUI dashboard and requires a terminal. |
--detect, -d | false | Enables threat detection in the root report. |
--remote-url | none | HTTP endpoint for Elasticsearch/OpenSearch bulk or Loki output; required for those formats. |
--remote-index | caddy-analyzer | Elasticsearch/OpenSearch index name. |
--remote-user, --remote-password | none | HTTP Basic credentials for remote delivery. |
--remote-token | none | HTTP Bearer token for remote delivery. |
--remote-batch-size | 100 | Documents per remote request. |
--remote-retries | 3 | Retries after the first remote delivery attempt. |
--remote-backoff | 250ms | Initial delay between remote retries. |
--remote-timeout | 10s | Timeout for each remote request. |
Subcommand-specific flags
tail
tail --detect highlights suspicious requests inline. All global filters remain available, but tail does not aggregate a report and never blocks IPs.
top
top --by DIMENSION is the flag form of the positional dimension. Valid dimensions are path, ip, ua, status, method, host, bandwidth, country, city and asn. The last three require a compatible GeoIP database.
baseline save
baseline save --detect adds security statistics to the JSON. --output is the global flag used to retain the file; generate the baseline with the same filters used for comparison.
blocklist
| Flag | What it does |
|---|---|
--cache-dir | Directory for downloaded feed files. |
--no-default-blocklists | Disables the eight built-in feeds for a custom-only setup. |
--blocklist-config FILE | Adds JSON sources with name, url and optional format; repeatable. |
--blocklist-remove NAME | Removes named sources; accepts comma-separated names. |
--format table|json | Output format for list and config. |
refresh downloads feeds; list shows status, age, errors and counts; config prints effective configuration; init saves current settings. None changes firewall rules.
guard
| Flag | Default | What it does |
|---|---|---|
--limit, -l | 100 | HTTP requests per IP in the window before blocking; 0 disables. |
--window, -w | 1m | Sliding monitoring window with per-second buckets. |
--duration, -d | 10m | Block duration; 0 is permanent. |
--auth-limit | 10 | 401/403 responses before blocking; 0 disables. |
--notfound-limit | 50 | 404 responses before blocking; 0 disables. |
--detect-confidence | 8 | Minimum pattern confidence from 1-10; 0 disables only this block path. |
--firewall-backend | auto | Selects auto, iptables, docker, nftables or hybrid. |
--dry-run | false | Reports and audits would-be blocks without changing firewall or state. |
--country-block | none | Immediately blocks listed ISO countries through GeoIP. |
--no-blocklist | false | Disables checking the eight feeds. |
--blocklist-refresh | 6h | Feed refresh interval; minimum 1h, 0 disables. |
--cache-dir | user cache | Blocklist cache directory. |
--never-block | none | Protected IPs/CIDRs; repeatable. |
--never-block-file | none | One IP/CIDR per line, with # comments. |
--state-file | /var/lib/caddy-analyzer/blocked.json | Persists and recovers blocks; empty disables. |
--iptables-timeout | 10s | Timeout for each firewall invocation. |
--subnet-limit | 0 | Blocks a /24 or /64 when distributed requests exceed the threshold. |
--rps-anomaly | 0 | Audits an RPS spike over the EWMA baseline; it is not a block. |
--cred-stuffing-limit | 0 | Audits distinct IPs failing the same path; it is not a block. |
--audit-log | JSONL in /var/log | Local event log; empty disables. |
--audit-syslog | disabled | UDP syslog destination such as 127.0.0.1:514. |
--webhook-url / --audit-webhook | disabled | Generic, Slack, Discord or PagerDuty notifications. |
--webhook-provider | generic | Webhook provider name. |
--pagerduty-routing-key | none | PagerDuty routing key; excluded from logs. |
--audit-timeout / --audit-retries | 5s / 2 | Sink timeout and retries after the first attempt; max 5 retries. |
--audit-rate-limit | 0 | Minimum interval between notifications for one IP; 0 disables. |
block and unban
Both commands share audit flags: --audit-log, --audit-syslog, --webhook-url/--audit-webhook, --webhook-provider, --pagerduty-routing-key, --audit-timeout, --audit-retries and --audit-rate-limit. --state-file synchronizes manual changes with guard. Additionally, unban --list lists active IPs and unban --all removes all tracked blocks; do not combine them.
config
--global, -g selects the global configuration file instead of the local one. config SOURCE and config set SOURCE set a source; config show displays it and config reset removes the selected file.
whitelist
--file selects the file, --init creates it, --add inserts IPs/CIDRs, --remove deletes them and --list, -l prints entries. Pass the same file to guard with --never-block-file.
update
--check only checks, --version TAG installs an exact release, --force allows reinstall or downgrade and --install-dir DIR selects the destination directory. cosign and SHA256 verification is mandatory.
export-sigma
Accepts at most one output file. No argument or - writes multi-document Sigma YAML to stdout; a path writes the complete rule set. Global analysis filters do not change the exported rules.
help and --help
caddy-analyze help COMMAND displays command help without executing it; --help is available on every command and subcommand. The help generated by the installed binary is the final authority for flags and defaults in that version.