Caddy Server Configuration Reference

Type: Software Reference Confidence: 0.92 Sources: 7 Verified: 2026-02-28 Freshness: 2026-02-28

TL;DR

Constraints

Quick Reference

DirectivePurposeExample
reverse_proxyProxy to backendreverse_proxy localhost:3000
file_serverServe static filesfile_server or file_server browse
rootDocument rootroot * /var/www/html
tlsConfigure TLStls [email protected]
encodeCompressionencode gzip zstd
headerResponse headersheader X-Frame-Options DENY
logAccess logslog { output file /var/log/caddy/access.log }
basicauthHTTP basic authbasicauth /admin/* { ... }
redirHTTP redirectredir https://new.example.com{uri}
handleExclusive route grouphandle /api/* { ... }
handle_pathHandle + strip prefixhandle_path /api/* { ... }
importInclude snippetsimport common_headers
lb_policyLoad balancinglb_policy least_conn
php_fastcgiPHP FastCGIphp_fastcgi localhost:9000

Decision Tree

START
├── Need automatic HTTPS with zero config?
│   ├── YES → Use domain name as site address (auto-provisioned)
│   └── NO ↓
├── Reverse proxy to single backend?
│   ├── YES → example.com { reverse_proxy localhost:3000 }
│   └── NO ↓
├── Multiple backends?
│   ├── YES → reverse_proxy with multiple upstreams + lb_policy
│   └── NO ↓
├── Serving static files?
│   ├── YES → root * /path + file_server
│   └── NO ↓
├── PHP application?
│   ├── YES → php_fastcgi localhost:9000
│   └── NO ↓
└── DEFAULT → reverse_proxy + file_server combo

Step-by-Step Guide

1. Install Caddy

Install on your platform. [src6]

# Ubuntu/Debian
sudo apt install caddy

# macOS
brew install caddy

# Docker
docker run -d -p 80:80 -p 443:443 -v caddy_data:/data caddy:2

Verify: caddy version → v2.8.x

2. Create basic Caddyfile

Write minimal reverse proxy. [src1]

example.com {
    reverse_proxy localhost:3000
}

Verify: caddy validate → Valid configuration

3. Add static files and security headers

Serve static assets alongside proxy. [src4]

example.com {
    handle /static/* {
        root * /var/www
        file_server
    }
    handle {
        reverse_proxy localhost:3000
    }
    encode gzip zstd
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        -Server
    }
}

Verify: caddy reload

4. Configure load balancing

Multiple backends with health checks. [src2]

api.example.com {
    reverse_proxy {
        to localhost:3001
        to localhost:3002
        lb_policy least_conn
        health_uri /health
        health_interval 10s
    }
}

Verify: caddy reload, check balanced traffic

Code Examples

SPA with API proxy

# Input:  React/Vue SPA + backend API
# Output: SPA files served, /api proxied to backend

example.com {
    handle_path /api/* {
        reverse_proxy localhost:4000
    }
    handle {
        root * /var/www/spa
        try_files {path} /index.html
        file_server
    }
    encode gzip
}

PHP with FastCGI

# Input:  PHP app (Laravel, WordPress)
# Output: Caddy serving PHP via FastCGI

example.com {
    root * /var/www/html
    php_fastcgi localhost:9000
    file_server
    encode gzip

    @blocked path /.env /.git/*
    respond @blocked 404
}

Multi-site with shared snippets

# Input:  Multiple sites
# Output: Shared security headers via snippet

(security) {
    header {
        X-Frame-Options "SAMEORIGIN"
        Strict-Transport-Security "max-age=63072000"
        -Server
    }
}

app.example.com {
    import security
    reverse_proxy localhost:3000
}

api.example.com {
    import security
    reverse_proxy localhost:4000
}

Anti-Patterns

Wrong: Manual TLS when auto-HTTPS works

# ❌ BAD — unnecessary manual TLS
example.com {
    tls /etc/ssl/cert.pem /etc/ssl/key.pem
}

Correct: Let Caddy handle HTTPS automatically

# ✅ GOOD — auto-provision, manage, and renew
example.com {
    reverse_proxy localhost:3000
}

Wrong: IP address expecting HTTPS

# ❌ BAD — ACME doesn't issue certs for IPs
192.168.1.100 {
    reverse_proxy localhost:3000
}

Correct: Use domain name

# ✅ GOOD — domain triggers auto-HTTPS
app.example.com {
    reverse_proxy localhost:3000
}

Wrong: Missing Docker volumes

# ❌ BAD — certificates lost on restart
services:
  caddy:
    image: caddy:2
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile

Correct: Persist /data and /config

# ✅ GOOD — certificates survive restarts
services:
  caddy:
    image: caddy:2
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
volumes:
  caddy_data:
  caddy_config:

Common Pitfalls

Diagnostic Commands

# Validate Caddyfile
caddy validate --config /etc/caddy/Caddyfile

# Start in foreground
caddy run --config /etc/caddy/Caddyfile

# Reload config (zero-downtime)
caddy reload --config /etc/caddy/Caddyfile

# List loaded modules
caddy list-modules

# Check if ports available
ss -tlnp | grep -E ':(80|443)'

# Test HTTPS
curl -I https://example.com

Version History & Compatibility

VersionStatusBreaking ChangesMigration Notes
Caddy 2.8.xCurrentNoneRecommended version
Caddy 2.7.xSupportedNone
Caddy 2.0GA (2020)Complete rewrite from v1Caddyfile syntax incompatible with v1
Caddy 1.xEOLMust migrate to v2

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Automatic HTTPS with zero configGranular control over every settingnginx
Quick reverse proxyHigh-perf CDN (millions req/s)nginx + CloudFront
Docker/K8s easy configExtensive module ecosystemnginx
PHP hosting with FastCGIRaw TCP/UDP proxyingHAProxy
Dev HTTPS with local CAEnterprise WAFnginx + ModSecurity

Important Caveats

Related Units