Caddy Traffic Capture
This guide shows you how to use Qtap to capture HTTP traffic flowing through Caddy, a modern web server with automatic HTTPS. You'll learn how to observe both incoming client requests and outgoing upstream connections from your Caddy server, all without proxies or code changes.
What You'll Learn
Capture Caddy ingress traffic (client requests)
Capture Caddy egress traffic (upstream service requests)
Monitor both sides of a reverse proxy simultaneously
Apply conditional capture rules for specific routes
Handle Caddy's automatic HTTPS with Qtap's TLS inspection
Set up Caddy + Qtap in Docker for testing
Deploy production-ready configurations
Use Cases
Why capture Caddy traffic?
Reverse Proxy Visibility: See both client requests and backend responses
API Gateway Monitoring: Track all API calls through your Caddy gateway
Automatic HTTPS Inspection: See inside TLS traffic without managing certificates
Microservices Debugging: Debug issues between services
Performance Analysis: Measure latency at each hop
Security Auditing: Monitor for suspicious traffic patterns
Migration Planning: Understand traffic patterns before infrastructure changes
Prerequisites
Linux system with kernel 5.10+ and eBPF support
Docker installed (for this guide's examples)
Root/sudo access
Basic understanding of Caddy/Caddyfile syntax
Part 1: Simple Caddy Web Server
Let's start with a basic Caddy setup serving static content and reverse proxying to upstream services.
Step 1: Create Caddy Configuration
Create a directory for our demo:
Create Caddyfile:
Step 2: Create Qtap Configuration
Create qtap.yaml:
Key Configuration Points:
direction: all- Captures both client→caddy AND caddy→upstream trafficignore_loopback: false- Important! Caddy often uses localhostlevel: full- Captures complete requests/responses including bodies
Step 3: Create Docker Compose Setup
Create docker-compose.yaml:
Part 2: Running and Testing
Step 1: Start the Services
Step 2: Generate Test Traffic
Step 3: View Captured Traffic
What you should see:
Key indicators that it's working:
✅
"exe": "/usr/bin/caddy"or similar - Caddy process identified✅
Direction: INGRESS- Client to Caddy✅
Direction: EGRESS- Caddy to upstream✅ Two transactions for proxied requests (ingress + egress)
✅ Custom headers visible (
X-Request-ID,X-Forwarded-Server)✅ Full request/response bodies captured
✅ Latency tracked for both hops
Part 3: Advanced Configurations
Configuration 1: Capture Only Errors
Reduce volume by capturing only failed requests:
Test it:
Configuration 2: Route-Specific Capture
Capture different levels for different Caddy routes using Rulekit:
Configuration 3: HTTPS Upstream Monitoring
When Caddy proxies to HTTPS upstreams, Qtap can still see the traffic:
Caddyfile:
qtap.yaml:
Why this works: Qtap hooks into Caddy's TLS library (typically Go's crypto/tls) before encryption happens, so it sees plaintext even for HTTPS upstreams.
Configuration 4: Production Setup with S3
For production, store sensitive data securely:
Update docker-compose.yaml:
Part 4: Real-World Use Cases
Use Case 1: API Gateway with Authentication
Monitor API gateway with focus on authentication and errors:
Caddyfile:
qtap.yaml:
Use Case 2: Microservices Mesh Monitoring
Monitor Caddy as a service mesh proxy:
Caddyfile:
qtap.yaml:
Use Case 3: Static Site with CDN Backend
Monitor Caddy serving static sites with CDN backend:
Caddyfile:
qtap.yaml:
Understanding the Output
Dual Capture for Reverse Proxy
When Caddy proxies a request, Qtap captures two HTTP transactions:
Transaction 1: INGRESS (Client → Caddy)
Transaction 2: EGRESS (Caddy → Upstream)
This lets you:
Measure total latency vs. backend latency
See how Caddy transforms requests (headers, paths)
Debug issues on either side of the proxy
Caddy-Specific Details
Process Identification:
Look for
execontainingcaddy(often/usr/bin/caddyor/usr/local/bin/caddy)Container name:
caddy-demo(in Docker)
Automatic HTTPS:
When Caddy uses automatic HTTPS, Qtap still sees plaintext via eBPF TLS hooks
No certificate management needed
Works with Let's Encrypt, ZeroSSL, or custom CAs
Troubleshooting
Not Seeing Caddy Traffic?
Check 1: Is Qtap running before requests?
Check 2: Is ignore_loopback correct?
Check 3: Is Caddy processing requests?
Check 4: Verify Qtap hooks Caddy
Seeing "l7Protocol": "other"?
"l7Protocol": "other"?This means connection captured but HTTP not parsed:
Wait longer after starting Qtap (6+ seconds)
Check if Caddy is using HTTP/3 (QUIC) - not yet supported
Verify traffic is actually HTTP/HTTPS
Caddy Using HTTP/3?
Qtap currently supports HTTP/1.x and HTTP/2. If Caddy negotiates HTTP/3 (QUIC):
Disable HTTP/3 in Caddyfile:
Too Much Traffic?
Option 1: Conditional capture
Option 2: Filter specific routes
Option 3: Summary level only
Performance Considerations
Caddy + Qtap Performance
Qtap operates out-of-band with minimal overhead:
CPU: ~1-3% for typical traffic
Memory: ~50-200MB depending on volume
Latency: Zero additional latency (passive observation)
Best practices for high-traffic Caddy:
Use
level: summaryordetailsfor high volumeApply conditional rules to capture selectively
Filter health checks and monitoring endpoints
Send to S3 with batching (use Fluent Bit)
Set TTL policies on storage (90 days recommended)
Scaling Recommendations
Traffic Volume
Recommended Level
Storage
< 100 req/sec
full
stdout or S3
100-1000 req/sec
details
S3 with batching
1000-10000 req/sec
summary
S3 + Fluent Bit
> 10000 req/sec
conditional rules
S3 + Fluent Bit + aggressive filtering
Caddy vs NGINX: Key Differences
Process Name:
Caddy:
/usr/bin/caddyNGINX:
/usr/sbin/nginx
Configuration:
Caddy: Caddyfile (simpler, more human-readable)
NGINX: nginx.conf (more complex, more options)
HTTPS:
Caddy: Automatic by default (Qtap still works!)
NGINX: Manual configuration
Language:
Caddy: Written in Go (uses Go's crypto/tls)
NGINX: Written in C (uses OpenSSL)
Both work perfectly with Qtap's eBPF-based capture.
Next Steps
Learn More About Qtap:
Traffic Capture Settings - Complete
tapconfigurationTraffic Processing with Plugins - All plugin options
Complete Guide - Progressive tutorial
Production Deployment:
Storage Configuration - S3 setup guide
Capturing All HTTP Traffic with Fluent Bit - Batching for scale
Kubernetes Manifest - Deploy in K8s
Related Guides:
Capturing NGINX Traffic - Similar guide for NGINX
Ingress Traffic Capture with Python - Application server capture
HTTPS Header Capture Without Proxies - TLS inspection details
Alternative: Cloud Management:
Qplane - Manage Qtap with visual dashboards
POC Kick Off Guide - Quick start
Cleanup
This guide uses validated configurations. All examples are tested and guaranteed to work with Caddy and Qtap.
Last updated