> caddy
Configure Caddy as a web server and reverse proxy — automatic HTTPS, reverse proxy, load balancing, file server, redirects, headers, rate limiting, and API configuration. Use when tasks involve serving websites, proxying to backend services, automatic TLS certificate management, or replacing Nginx with a simpler configuration.
curl "https://skillshub.wtf/TerminalSkills/skills/caddy?format=md"Caddy
Modern web server with automatic HTTPS. Configures TLS certificates from Let's Encrypt without any setup — just specify domain names and Caddy handles the rest.
Setup
# Debian/Ubuntu
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
# Docker
docker run -d -p 80:80 -p 443:443 \
-v caddy_data:/data -v caddy_config:/config \
-v $PWD/Caddyfile:/etc/caddy/Caddyfile \
caddy:latest
Caddyfile (config format)
Simple Static Site
example.com {
root * /var/www/html
file_server
encode gzip zstd
}
That's it. Caddy automatically obtains and renews a TLS certificate for example.com.
Reverse Proxy
# Single backend
app.example.com {
reverse_proxy localhost:3000
}
# With health checks and load balancing
api.example.com {
reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
lb_policy round_robin
health_uri /health
health_interval 10s
health_timeout 5s
}
}
# WebSocket support (automatic — no special config needed)
ws.example.com {
reverse_proxy localhost:8080
}
Multi-Service Setup
# Main app
example.com {
# API routes → backend
handle /api/* {
reverse_proxy localhost:3000
}
# Static assets → file server with caching
handle /static/* {
root * /var/www/static
file_server
header Cache-Control "public, max-age=31536000, immutable"
}
# Everything else → frontend SPA
handle {
root * /var/www/app
try_files {path} /index.html
file_server
}
encode gzip zstd
}
# Admin panel — separate subdomain
admin.example.com {
reverse_proxy localhost:3001
# Basic auth protection
basicauth {
admin $2a$14$... # bcrypt hash: caddy hash-password
}
}
# Redirect www to non-www
www.example.com {
redir https://example.com{uri} permanent
}
Security Headers
example.com {
reverse_proxy localhost:3000
header {
# Security headers
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Remove server identification
-Server
}
}
Rate Limiting
example.com {
# Rate limit: 100 requests per minute per IP
rate_limit {
zone api_zone {
key {remote_host}
events 100
window 1m
}
}
reverse_proxy localhost:3000
}
CORS
api.example.com {
@cors_preflight method OPTIONS
handle @cors_preflight {
header Access-Control-Allow-Origin "https://app.example.com"
header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
header Access-Control-Allow-Headers "Content-Type, Authorization"
header Access-Control-Max-Age "86400"
respond "" 204
}
header Access-Control-Allow-Origin "https://app.example.com"
reverse_proxy localhost:3000
}
JSON Config (API mode)
For programmatic configuration instead of Caddyfile:
{
"apps": {
"http": {
"servers": {
"main": {
"listen": [":443"],
"routes": [
{
"match": [{"host": ["api.example.com"]}],
"handle": [{
"handler": "reverse_proxy",
"upstreams": [
{"dial": "localhost:3000"},
{"dial": "localhost:3001"}
],
"load_balancing": {"selection_policy": {"policy": "round_robin"}},
"health_checks": {
"active": {"uri": "/health", "interval": "10s"}
}
}]
}
]
}
}
}
}
}
Config API
Caddy exposes a REST API for live configuration changes — no restarts needed:
# Load full config
curl -X POST http://localhost:2019/load \
-H "Content-Type: application/json" \
-d @caddy-config.json
# Get current config
curl http://localhost:2019/config/
# Update a specific route without touching anything else
curl -X PATCH "http://localhost:2019/config/apps/http/servers/main/routes/0" \
-H "Content-Type: application/json" \
-d '{"handle": [{"handler": "reverse_proxy", "upstreams": [{"dial": "localhost:3002"}]}]}'
Common Patterns
Local Development with HTTPS
# Caddyfile.dev — local HTTPS with self-signed cert
localhost {
tls internal # Auto-generate a local CA cert
reverse_proxy /api/* localhost:3000
reverse_proxy localhost:5173 # Vite dev server
}
caddy run --config Caddyfile.dev
# App available at https://localhost with valid (local) TLS
Wildcard Subdomains
# Requires DNS challenge (e.g., Cloudflare DNS plugin)
*.example.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
# Route subdomains to different backends
@app host app.example.com
handle @app {
reverse_proxy localhost:3000
}
@docs host docs.example.com
handle @docs {
reverse_proxy localhost:3001
}
# Catch-all: 404
handle {
respond "Not found" 404
}
}
Caching Proxy
api.example.com {
reverse_proxy localhost:3000
# Cache responses from the backend
@cacheable {
method GET
path /api/public/*
}
header @cacheable Cache-Control "public, max-age=300" # 5 min cache
}
Caddy vs Nginx
| Caddy | Nginx | |
|---|---|---|
| HTTPS | Automatic (Let's Encrypt) | Manual cert setup or certbot |
| Config format | Caddyfile (simple) or JSON | nginx.conf (complex) |
| Hot reload | API-based, zero downtime | nginx -s reload |
| HTTP/3 | Built-in | Requires compilation flags |
| Plugins | Go modules, xcaddy build | C modules, recompilation |
| Performance | Fast, slightly slower than Nginx | Fastest for static files |
| Use case | Modern apps, auto-HTTPS | Legacy, high-traffic static |
Commands
caddy run # Foreground with Caddyfile in current dir
caddy start # Background daemon
caddy stop # Stop daemon
caddy reload # Reload config without downtime
caddy adapt --config Caddyfile # Convert Caddyfile to JSON (debugging)
caddy validate --config Caddyfile # Check for syntax errors
caddy hash-password # Generate bcrypt hash for basicauth
caddy fmt --overwrite Caddyfile # Format Caddyfile
Guidelines
- Automatic HTTPS is the default — don't disable it unless you have a specific reason. Caddy handles cert issuance, renewal, and OCSP stapling.
- Use
try_filesfor SPAs —try_files {path} /index.htmlserves the SPA shell for all routes, letting the frontend router handle them. - Caddyfile for humans, JSON for automation — Caddyfile is easier to read and maintain; JSON API is for programmatic management.
- Config API is live — changes via the admin API take effect immediately without restart or reload.
- DNS challenge for wildcards — wildcard certs require DNS-01 challenge. Install the appropriate DNS provider plugin via
xcaddy. encode gzip zstdon every site — compression is free performance. Zstandard is faster than gzip for modern clients.- Health checks on reverse proxy — always configure them for multi-backend setups to avoid routing to dead backends.
-Serverheader — remove it in production to avoid leaking server info.
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zoho
Integrate and automate Zoho products. Use when a user asks to work with Zoho CRM, Zoho Books, Zoho Desk, Zoho Projects, Zoho Mail, or Zoho Creator, build custom integrations via Zoho APIs, automate workflows with Deluge scripting, sync data between Zoho apps and external systems, manage leads and deals, automate invoicing, build custom Zoho Creator apps, set up webhooks, or manage Zoho organization settings. Covers Zoho CRM, Books, Desk, Projects, Creator, and cross-product integrations.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> zipkin
Deploy and configure Zipkin for distributed tracing and request flow visualization. Use when a user needs to set up trace collection, instrument Java/Spring or other services with Zipkin, analyze service dependencies, or configure storage backends for trace data.