Production-hardened Caddy web server with Coraza WAF and OWASP CRS - A secure, performant, and easy-to-deploy web application firewall solution for modern applications.
Developmi Enterprise Edition • Curated by Miguel Lozano • GitHub • Container Registry
- Non-root execution: Runs as
caddyuser (UID 1337) - no root privileges - Supply chain security: Pinned versions, SHA256 verification of OWASP CRS rules
- Multi-stage builds: Minimal attack surface, optimized layers
- Health monitoring: Process verification healthcheck
- Structured logging: JSON logs for SIEM integration
- Coraza WAF v2.2.0: Modern, high-performance web application firewall
- OWASP CRS v4.23.0: Latest Core Rule Set with 290+ protection rules
- DetectionOnly by default: Prevents false positives in new deployments
- Audit logging: JSON audit logs to stdout for easy monitoring
- Rate limiting: Built-in rate limiting plugin for DDoS protection
- Optimized Alpine base: Small footprint (~45MB compressed)
- TLS by default: Automatic Let's Encrypt integration
- Multi-architecture: Supports linux/amd64 and linux/arm64
- Cloud-native: Perfect for Kubernetes, Docker Swarm, and standalone Docker
docker pull ghcr.io/miguel-devops/caddy-waf:v1.0.0cp .env.example .env
# Edit .env with your domain/backend/image valuescp Caddyfile.example Caddyfile
# Edit Caddyfile for your domain and upstreamsdocker build -t your-registry/your-caddy-waf:custom \
--build-arg CORAZA_CADDY_REF=v2.2.0 \
--build-arg CADDY_RATELIMIT_REF=v0.1.0 \
--build-arg CADDY_DNS_CLOUDFLARE_REF=v0.2.3 \
.Then set CADDY_WAF_IMAGE=your-registry/your-caddy-waf:custom in .env.
# Caddyfile - Save this as Caddyfile in the same directory as docker-compose.yml
{
order coraza_waf first
}
yourdomain.com {
respond "Caddy with Coraza WAF is running" 200
}docker compose up -dThe WAF operates in three modes (configured in Caddyfile):
- DetectionOnly (Default): Logs attacks without blocking - perfect for initial deployment
- On: Active protection - blocks malicious requests
- Off: Disables WAF completely
Recommended rollout for production:
- Keep
SecRuleEngine DetectionOnlyduring the initial observation window. - Review audit logs and tune CRS exclusions based on real traffic.
- Switch to
SecRuleEngine Ononly after the application has run long enough to establish a stable false-positive baseline (commonly 7-14 days, depending on traffic diversity and release cadence).
{
email [email protected]
order coraza_waf first
# JSON logging for observability
log {
output stdout
format json
}
}
(waf) {
coraza_waf {
directives `
Include /etc/caddy/coraza.conf
Include /etc/caddy/owasp-crs/crs-setup.conf
Include /etc/caddy/owasp-crs/rules/*.conf
# Start with DetectionOnly, change to On after tuning
SecRuleEngine DetectionOnly
# Audit logging
SecAuditEngine RelevantOnly
SecAuditLog /dev/stdout
SecAuditLogFormat JSON
`
}
}
# Your site configuration
example.com {
import waf
reverse_proxy backend:8080
}For detailed WAF tuning, rule exceptions, and performance optimization, see the complete TUNING GUIDE.
Project roadmap and planned security integrations are tracked in ROADMAP.md.
Mount your custom rules directory:
volumes:
- ./custom-crs:/etc/caddy/owasp-crs| Variable | Default | Description |
|---|---|---|
ACME_EMAIL |
(empty) | Email for Let's Encrypt certificates |
SITE_ADDRESS |
localhost |
Site address/server name used by Caddy |
BACKEND_UPSTREAM |
example-app:80 |
Reverse proxy backend upstream |
CADDY_WAF_IMAGE |
ghcr.io/miguel-devops/caddy-waf:v1.0.0 |
Caddy WAF image reference |
EXAMPLE_APP_IMAGE |
containous/whoami:latest |
Demo backend image |
CADDY_ADAPTER |
caddyfile |
Configuration adapter to use |
github.com/corazawaf/coraza-caddy/[email protected]- Coraza WAF integrationgithub.com/mholt/[email protected]- Rate limitinggithub.com/caddy-dns/[email protected]- Cloudflare DNS for ACME
# Check container health
docker ps --filter "name=caddy-waf"
# View logs
docker logs caddy-waf
# Test WAF is working
curl -I https://yourdomain.com# Scan image with Trivy
docker run --rm aquasec/trivy image ghcr.io/miguel-devops/caddy-waf:v1.0.0
# Scan with Docker Scout
docker scout quickview ghcr.io/miguel-devops/caddy-waf:v1.0.0{
"level": "info",
"ts": 1678901234.567,
"logger": "http.log.access",
"msg": "handled request",
"request": {
"method": "GET",
"uri": "/test",
"proto": "HTTP/2",
"remote_ip": "192.168.1.100"
},
"waf_action": "detected",
"waf_rule_id": "941100"
}coraza_waf_processed_total- Total requests processedcoraza_waf_blocked_total- Requests blocked by WAFcoraza_waf_rules_triggered- Rules triggered (by ID)
- GitHub Issues: Report bugs or request features
- Documentation: TUNING.md for advanced configuration
For enterprise support, custom configurations, or security consulting:
- Website: developmi.com
- Email: [email protected]
- GitHub: Miguel-DevOps
MIT License - See LICENSE file for details.
- Caddy Server - Amazing web server with automatic HTTPS
- Coraza WAF - Enterprise-grade WAF engine
- OWASP Core Rule Set - Industry-standard protection rules
- Developmi - DevOps & Security consulting
Maintained with ❤️ by Miguel Lozano