Introduction
Caddy is a modern, modular web server that stands out for its automatic HTTPS capability and simplicity. Written in Go, Caddy was designed from the ground up to be secure-by-default, automatic, and easy to configure. In 2026, with HTTPS adoption reaching near-universal levels and TLS configuration becoming increasingly complex, Caddy’s philosophy of “HTTPS by default” has never been more relevant.
This comprehensive guide covers Caddy’s unique features, configuration syntax, reverse proxy capabilities, advanced TLS management, and production deployment strategies. Whether you’re seeking a simple development server or a production-grade TLS termination point, Caddy offers a compelling solution.
What is Caddy?
Caddy is an open-source web server with automatic HTTPS written in Go. Its defining characteristic is making TLS configuration effortless while providing all the features expected from a production web server.
Key Features
Automatic HTTPS: Caddy obtains and renews TLS certificates automatically for all sites.
HTTP/3 Support: Native HTTP/3 support out of the box.
Reverse Proxy: Built-in powerful reverse proxy with load balancing and health checks.
FastCGI: Native support for PHP, Python, and other FastCGI applications.
WebSockets: Full WebSocket proxy support.
Markdown Rendering: Serve Markdown files with automatic rendering.
Minimal Configuration: Most sites work with minimal or no configuration.
Installation
Binary Installation
# Download Caddy
curl -o caddy https://getcaddy.com?softcare=tar.gz
# Or use official installer
curl https://getcaddy.com | bash -s personal
# Verify installation
caddy version
Package Managers
# macOS
brew install caddy
# 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
# Docker
docker pull caddy:2
Building from Source
# Install Go
curl -sL https://golang.org/dl/go1.21.linux-amd64.tar.gz | sudo tar -C /usr/local -xzf -
# Clone and build
git clone https://github.com/caddyserver/caddy.git
cd caddy/cmd/caddy/
go install
Configuration
Caddyfile Syntax
Caddy’s configuration format (Caddyfile) is designed for readability:
# Simple static site
example.com {
root * /var/www
file_server
}
# Reverse proxy
api.example.com {
reverse_proxy localhost:8080
}
# With TLS
tls [email protected]
Basic Static Site
example.com {
# Serve static files from /var/www
root * /var/www
# Enable static file server
file_server
# Enable gzip compression
encode gzip
# Parse HTML templates
templates
}
Multiple Sites
# Site 1: Static files
www.example.com {
root * /var/www/frontend
file_server
encode gzip
}
# Site 2: API
api.example.com {
reverse_proxy localhost:3000
}
# Site 3: PHP-FPM
blog.example.com {
root * /var/www/wordpress
php_fastcgi localhost:9000
}
Automatic HTTPS
Caddy’s automatic HTTPS is its standout feature.
How It Works
# No configuration needed - HTTPS automatic
example.com {
root * /var/www
file_server
}
Caddy will:
- Obtain TLS certificate from Let’s Encrypt on first request
- Redirect HTTP to HTTPS automatically
- Renew certificates 30 days before expiration
- Handle OCSP stapling
- Support HTTP/3
Custom TLS Configuration
example.com {
tls {
dns cloudflare
protocols tls1.3
ciphers TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256
}
root * /var/www
file_server
}
TLS with Custom Certificate
example.com {
tls /etc/ssl/certs/server.crt /etc/ssl/private/server.key
# Or with internal CA
tls {
ca /path/to/ca.crt
ca_root /path/to/ca-root.crt
}
root * /var/www
file_server
}
Mutual TLS (mTLS)
example.com {
tls {
client_ca /etc/ssl/certs/client-ca.crt
verify_client_if_given
}
reverse_proxy localhost:8080
}
Reverse Proxy
Caddy includes a powerful reverse proxy implementation.
Basic Reverse Proxy
example.com {
reverse_proxy localhost:8080
}
Load Balancing
example.com {
reverse_proxy {
to localhost:8080 localhost:8081 localhost:8082
health_path /health
health_interval 30s
health_timeout 5s
lb_try_duration 5s
}
}
Load Balancing Methods
example.com {
# Round robin (default)
reverse_proxy {
to backend1:8080 backend2:8080 backend3:8080
}
# Random
reverse_proxy {
to backend1:8080 backend2:8080
lb_policy random
}
# IP hash (sticky sessions)
reverse_proxy {
to backend1:8080 backend2:8080
lb_policy ip_hash
}
# Header-based
reverse_proxy {
to backend1:8080 backend2:8080
lb_policy header X-User-ID
}
}
Header Manipulation
example.com {
reverse_proxy localhost:8080 {
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote}
header_up Host {host}
header_down X-Powered-By "Caddy"
}
}
WebSocket Proxying
example.com {
reverse_proxy localhost:8080 {
header_up Upgrade {>Upgrade}
header_up Connection {>Connection}
}
}
FastCGI Support
Caddy has native FastCGI support for PHP, Python, and other languages.
PHP-FPM
example.com {
root * /var/www
php_fastcgi localhost:9000
file_server
}
PHP with Custom Socket
example.com {
root * /var/www
php_fastcgi unix//run/php/php8.1-fpm.sock
encode gzip zstd
file_server
}
Python WSGI
example.com {
root * /var/www
reverse_proxy unix//run/gunicorn.sock {
header_up Host {host}
}
}
Advanced Features
URL Rewriting
example.com {
# Simple rewrite
rewrite /old /new
# Regex rewrite
@blog {
path /blog/*
}
rewrite @blog /blog/index.php?p={path}
# Query parameter manipulation
@old {
query p=
}
redirect @old /new-page
}
Request Matching
example.com {
# Path matching
@api {
path /api/*
}
reverse_proxy @api localhost:8080
# Header matching
@mobile {
header User-Agent *Mobile*
}
reverse_proxy @mobile localhost:8081
# Method matching
@posts {
path /posts/*
method POST PUT DELETE
}
reverse_proxy @posts localhost:8082
# Combined matching
@admin {
path /admin/*
header Authorization "Bearer *"
}
reverse_proxy @admin localhost:8083
}
Compression
example.com {
# Automatic compression
encode {
gzip
zstd
}
# Custom compression levels
encode gzip 6
# Match specific types
encode {
match {
header Content-Type text/*
header Content-Type application/json
header Content-Type application/javascript
}
gzip
}
}
Rate Limiting
example.com {
# Global rate limit
rate_limit {
zone generic {
key {remote_ip}
events 100
window 1s
}
}
reverse_proxy localhost:8080
}
Caching Headers
example.com {
root * /var/www
file_server
# Set cache headers
@static {
path *.css *.js *.ico *.svg *.jpg *.png
}
header @static {
Cache-Control "public, max-age=31536000, immutable"
}
# Remove headers
header {
-X-Powered-By
-Server
}
}
Container Deployment
Docker Compose
version: '3.8'
services:
caddy:
image: caddy:2
container_name: caddy
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./www:/var/www:ro
- caddy_data:/data
- caddy_config:/config
environment:
- CADDY_INGRESS_NETWORKS=caddy
restart: unless-stopped
volumes:
caddy_data:
caddy_config:
Docker with Custom Config
docker run -d --name caddy \
-p 80:80 \
-p 443:443 \
-p 443:443/udp \
-v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
-v $(pwd)/site:/var/www \
-v caddy_data:/data \
caddy:2
Production Configuration
Production Caddyfile
# Global options
{
admin off
email [email protected]
ocsp_stapling off
log {
output file /var/log/caddy/access.log
format console
}
}
# Main site
example.com {
root * /var/www
file_server
encode {
gzip
}
# Security headers
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
-X-Powered-By
}
# Handle .php files
php_fastcgi unix//run/php/php8.1-fpm.sock
# Log format
log {
output file /var/log/caddy/example.com.log
}
}
# API subdomain
api.example.com {
reverse_proxy localhost:3000 {
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
# Rate limiting
rate_limit {
zone api {
key {remote_ip}
events 1000
window 1s
}
}
}
# Admin panel (internal only)
admin.example.com {
internal
reverse_proxy localhost:8080
}
Systemd Service
[Unit]
Description=Caddy Web Server
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=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
Performance Tuning
Connection Limits
{
servers {
read_header_timeout 10s
read_timeout 60s
write_timeout 60s
idle_timeout 5m
max_header_bytes 32768
}
}
HTTP/3 Configuration
{
servers {
protocol {
experimental_http3
}
}
}
Go Runtime Tuning
# Set GOMAXPROCS
GOMAXPROCS=4 caddy run
# Memory tuning
GOGC=100 caddy run
Monitoring
Metrics Endpoint
example.com {
metrics /metrics
}
Structured Logging
example.com {
log {
output file /var/log/caddy/access.log
format json {
time_format "iso8601"
}
}
}
Best Practices
Security
- Use TLS 1.3 minimum
- Enable HSTS headers
- Remove sensitive headers
- Configure proper CSP headers
- Use internal routes for admin
Reliability
- Configure health checks for proxies
- Set appropriate timeouts
- Use multiple backend servers
- Enable request buffering for large uploads
Performance
- Enable HTTP/3
- Use compression
- Configure proper cache headers
- Monitor connection counts
Configuration
- Use Caddyfile for simplicity
- Version control configurations
- Test configurations before deployment
- Use includes for organization
Conclusion
Caddy web server represents a paradigm shift in web server configuration, making HTTPS and TLS management effortless while providing all the features needed for production deployments. Its automatic HTTPS, simple configuration syntax, and built-in reverse proxy capabilities make it an excellent choice for developers and operations teams alike.
Whether you’re running a simple static site, a complex multi-service architecture, or anything in between, Caddy’s philosophy of sensible defaults and automatic security provides an excellent foundation. As we move further into 2026 with increasingly complex security requirements, Caddy’s “HTTPS by default” approach positions it as the web server for the modern internet.
Comments