Caddy
Modern web server developed in Go. Eliminates complex SSL certificate management with automatic HTTPS functionality. Features zero-configuration SSL support and early HTTP/3 support.
Caddy Web Server
Caddy is a modern web server written in Go that eliminates complex SSL certificate management through automatic HTTPS functionality, achieving zero-configuration SSL support. With its developer-friendly design and comprehensive features, it's widely used from small projects to large-scale production environments.
Overview
Caddy is a powerful, modern web server that prioritizes security, simplicity, and performance. Built in Go, it provides automatic HTTPS, dynamic configuration capabilities, and extensive extensibility through its modular architecture. Caddy has served trillions of requests and managed millions of TLS certificates, proving its reliability in production environments.
Details
Caddy 2025 edition continues to maintain its position as the definitive modern web server solution. With automatic HTTPS powered by CertMagic, API-driven configuration management, and support for the latest protocols including HTTP/3 by default, Caddy represents the next generation of web server technology. The server is designed with zero external dependencies, written in Go for memory safety, and features a powerful module system that allows unlimited extensibility without bloat.
Key Features
- Automatic HTTPS: Zero-configuration SSL/TLS with Let's Encrypt integration
- Modern Protocol Support: HTTP/1.1, HTTP/2, and HTTP/3 by default
- Dynamic Configuration: API-driven configuration with zero-downtime reloads
- High Performance: Go-based implementation with low memory usage
- Extensible Architecture: Powerful module system for unlimited customization
- Production Ready: Proven scalability to hundreds of thousands of sites
Pros and Cons
Pros
- Revolutionary automatic HTTPS eliminates manual certificate management complexity
- Exceptional developer experience with intuitive Caddyfile syntax and JSON configuration
- Zero external dependencies with single binary deployment simplifying operations
- Modern protocol support (HTTP/3) by default providing cutting-edge performance
- Extensive module ecosystem enabling unlimited customization possibilities
- Strong security guarantees through Go's memory safety and secure defaults
- Enterprise-grade scalability proven with trillions of requests served
Cons
- Relatively newer compared to established servers like Apache or Nginx
- Smaller community and ecosystem compared to traditional web servers
- Go-specific module development may require learning curve for non-Go developers
- Some advanced configurations may require JSON instead of Caddyfile syntax
- Limited shared hosting provider support compared to traditional servers
- Performance optimization may require understanding of Go runtime characteristics
Reference Pages
- Caddy Official Website
- Caddy Documentation
- Caddy GitHub Repository
- Caddy Community Forum
- CertMagic Library
Code Examples
Installation and Basic Setup
# Install on Ubuntu/Debian
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
# Install on macOS with Homebrew
brew install caddy
# Install on Windows with Chocolatey
choco install caddy
# Install using Docker
docker pull caddy:2-alpine
# Verify installation
caddy version
Basic Configuration (Caddyfile)
# Simple static file server
example.com {
root * /var/www/html
file_server
}
# HTTPS with custom certificate
secure.example.com {
tls cert.pem key.pem
root * /var/www/secure
file_server
}
# Automatic HTTPS (default behavior)
auto.example.com {
respond "Hello, HTTPS World!"
}
# Multiple sites configuration
:8080 {
respond "Development server on port 8080"
}
:8081 {
root * /var/www/staging
file_server browse
}
# PHP application support
blog.example.com {
root * /var/www/wordpress
encode gzip
php_fastcgi unix//run/php/php8.2-fpm.sock
file_server
}
HTTPS Configuration and SSL Management
# Custom CA for development
{
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
dev.local {
tls internal # Use internal CA for local development
file_server
}
# DNS challenge for wildcard certificates
{
acme_dns cloudflare {
zone_token {env.CLOUDFLARE_ZONE_TOKEN}
dns_token {env.CLOUDFLARE_DNS_TOKEN}
}
}
*.example.com {
tls {
dns cloudflare
}
reverse_proxy localhost:3000
}
# On-demand TLS (use with caution)
{
on_demand_tls {
ask http://localhost:5555/check
}
}
https:// {
tls {
on_demand
}
reverse_proxy localhost:8080
}
# Custom cipher suites and TLS options
secure-api.example.com {
tls {
protocols tls1.2 tls1.3
ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
curves X25519 secp384r1
}
reverse_proxy localhost:9000
}
Performance Optimization Settings
# Global performance settings
{
# Enable HTTP/3 globally
servers {
protocol {
experimental_http3
}
}
# Connection limits and timeouts
servers {
max_header_size 16KB
read_timeout 10s
write_timeout 20s
idle_timeout 2m
}
}
# Site-specific optimization
example.com {
# Static asset optimization
@static {
path *.css *.js *.png *.jpg *.jpeg *.gif *.ico *.svg *.webp
}
header @static {
Cache-Control "public, max-age=31536000, immutable"
+Vary "Accept-Encoding"
}
# Compression
encode {
zstd
gzip 6
match {
header Content-Type text/*
header Content-Type application/json
header Content-Type application/javascript
header Content-Type application/xml
}
}
# Precompressed files
file_server {
precompressed zstd gzip
}
root * /var/www/html
file_server
}
# Connection pooling for reverse proxy
api.example.com {
reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
lb_policy least_conn
lb_try_duration 10s
lb_try_interval 250ms
fail_duration 30s
max_fails 3
health_path /health
health_interval 30s
health_timeout 5s
# Connection pooling
transport http {
keepalive 30s
keepalive_idle_conns 10
keepalive_idle_conns_per_host 2
}
}
}
Security Configuration
# Comprehensive security headers
{
header /* {
# HTTPS enforcement
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# XSS protection
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
X-XSS-Protection "1; mode=block"
# Content Security Policy
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'"
# Privacy headers
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Remove server info
-Server
}
}
# IP-based access control
admin.example.com {
@allowed {
remote_ip 192.168.1.0/24 10.0.0.0/8 172.16.0.0/12
}
handle @allowed {
basicauth {
admin $2a$14$hGL3... # bcrypt hash of password
}
reverse_proxy localhost:8080
}
handle {
respond "Access Denied" 403
}
}
# Rate limiting
api.example.com {
rate_limit {
zone api_zone
key {remote_host}
events 100
window 1m
count 1000
}
reverse_proxy localhost:3000
}
# Client certificate authentication
secure.example.com {
tls {
client_auth {
mode require_and_verify
trusted_ca_cert_file /path/to/ca.pem
}
}
reverse_proxy localhost:9000
}
Operational Management
# Health check endpoint
{
admin localhost:2019 {
origins localhost:2019
}
}
# Comprehensive logging
{
log {
output file /var/log/caddy/access.log {
roll_size 100mb
roll_keep 5
roll_keep_for 24h
}
format json
level INFO
}
log errors {
output file /var/log/caddy/errors.log
format json
level ERROR
}
}
# Site-specific logging with custom fields
example.com {
log {
output file /var/log/caddy/example.log
format json {
message_key "msg"
level_key "level"
time_key "ts"
name_key "logger"
caller_key "caller"
stacktrace_key "stacktrace"
}
include http.log.access
}
file_server
}
# Metrics and monitoring
{
servers {
metrics
}
}
example.com {
handle /metrics {
metrics
}
handle /health {
respond "OK" 200 {
header Content-Type "text/plain"
}
}
handle {
reverse_proxy localhost:8080
}
}
# Graceful reloads
# Use: caddy reload --config /etc/caddy/Caddyfile
# Or via API: curl localhost:2019/config/ -X POST -H "Content-Type: application/json" -d @new-config.json
Docker and Production Deployment
# docker-compose.yml for production
version: '3.8'
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
cap_add:
- NET_ADMIN
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
- "2019:2019" # Admin API (internal only)
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./site:/srv
- caddy_data:/data
- caddy_config:/config
- /var/log/caddy:/var/log/caddy
environment:
- ACME_AGREE=true
- CADDY_ADMIN=0.0.0.0:2019
networks:
- caddy
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:2019/config/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
app:
build: .
restart: unless-stopped
networks:
- caddy
depends_on:
- caddy
volumes:
caddy_data:
external: true
caddy_config:
networks:
caddy:
external: true
# Systemd service file (/etc/systemd/system/caddy.service)
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target