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.

web-servergohttpsreverse-proxyssltlsmodern

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

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