Caddy ist ein moderner Webserver mit automatischem HTTPS. Er bezieht und erneuert Let's Encrypt Zertifikate vollautomatisch und benötigt minimale Konfiguration.

Vorteile von Caddy

Features

- Automatisches HTTPS (Let's Encrypt)
- Automatische HTTP/2 und HTTP/3
- Einfache Caddyfile-Syntax
- Reverse Proxy integriert
- Zero-Downtime Reloads
- Modulares Design

Vergleich mit Nginx

| Feature | Caddy | Nginx | |---------|-------|-------| | HTTPS | Automatisch | Manuell/Certbot | | Konfiguration | Einfach | Komplex | | Performance | Gut | Exzellent | | Erweiterungen | Go Plugins | C Module |

Installation

Debian/Ubuntu

# Repository hinzufügen
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list

# Installieren
apt update
apt install caddy

Docker

docker run -d \
  --name caddy \
  -p 80:80 \
  -p 443:443 \
  -v caddy_data:/data \
  -v caddy_config:/config \
  -v ./Caddyfile:/etc/caddy/Caddyfile \
  caddy

Service

systemctl enable --now caddy
systemctl status caddy

Caddyfile Grundlagen

Minimale Konfiguration

# /etc/caddy/Caddyfile

example.com {
    root * /var/www/html
    file_server
}

Mit Reverse Proxy

app.example.com {
    reverse_proxy localhost:3000
}

Mehrere Sites

example.com {
    root * /var/www/example
    file_server
}

api.example.com {
    reverse_proxy localhost:8080
}

www.example.com {
    redir https://example.com{uri}
}

Reverse Proxy

Einfacher Proxy

app.example.com {
    reverse_proxy localhost:3000
}

Mit Header-Anpassung

app.example.com {
    reverse_proxy localhost:3000 {
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

Load Balancing

app.example.com {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy round_robin
        health_uri /health
        health_interval 10s
    }
}

Sticky Sessions

app.example.com {
    reverse_proxy localhost:3001 localhost:3002 {
        lb_policy cookie
    }
}

WebSocket

app.example.com {
    reverse_proxy localhost:3000

    @websocket {
        header Connection *Upgrade*
        header Upgrade websocket
    }
    reverse_proxy @websocket localhost:3000
}

Statische Dateien

File Server

example.com {
    root * /var/www/html
    file_server
}

Mit Index und Browse

files.example.com {
    root * /var/www/files
    file_server browse
}

SPA (Single Page Application)

app.example.com {
    root * /var/www/app
    try_files {path} /index.html
    file_server
}

Mit Komprimierung

example.com {
    root * /var/www/html
    encode gzip zstd
    file_server
}

SSL/TLS Konfiguration

Automatisches HTTPS

# HTTPS ist automatisch aktiviert für Domains
example.com {
    respond "Hello, HTTPS!"
}

Eigene Zertifikate

example.com {
    tls /etc/ssl/certs/cert.pem /etc/ssl/private/key.pem
    reverse_proxy localhost:3000
}

Interne CA (Self-Signed)

localhost {
    tls internal
    respond "Hello, localhost!"
}

On-Demand TLS

{
    on_demand_tls {
        ask http://localhost:5000/check
    }
}

https:// {
    tls {
        on_demand
    }
    reverse_proxy localhost:3000
}

Middleware und Direktiven

Basic Auth

admin.example.com {
    basicauth {
        admin $2a$14$... # bcrypt hash
    }
    reverse_proxy localhost:8080
}

Hash erstellen:

caddy hash-password

Rate Limiting

{
    order rate_limit before basicauth
}

api.example.com {
    rate_limit {remote.ip} 10r/s
    reverse_proxy localhost:8080
}

IP Filtering

admin.example.com {
    @allowed remote_ip 192.168.1.0/24 10.0.0.0/8
    handle @allowed {
        reverse_proxy localhost:8080
    }
    respond "Forbidden" 403
}

Headers

example.com {
    header {
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block"
        Strict-Transport-Security "max-age=31536000"
        -Server
    }
    reverse_proxy localhost:3000
}

Logging

example.com {
    log {
        output file /var/log/caddy/access.log
        format json
    }
    reverse_proxy localhost:3000
}

Rewrite und Redirect

Redirect

# HTTP zu HTTPS (automatisch)
# www zu non-www
www.example.com {
    redir https://example.com{uri} permanent
}

# Pfad-Redirect
example.com {
    redir /old-path /new-path permanent
    reverse_proxy localhost:3000
}

Rewrite

example.com {
    rewrite /api/* /api/v1{uri}
    reverse_proxy localhost:3000
}

Strip Prefix

example.com {
    handle_path /api/* {
        reverse_proxy localhost:8080
    }
}

PHP mit Caddy

PHP-FPM

example.com {
    root * /var/www/html
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server
}

Laravel

laravel.example.com {
    root * /var/www/laravel/public
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server
    encode gzip

    @static {
        path_regexp static \.(css|js|gif|ico|jpg|jpeg|png|svg|woff|woff2)$
    }
    header @static Cache-Control "public, max-age=31536000"
}

WordPress

wordpress.example.com {
    root * /var/www/wordpress
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server

    @disallowed {
        path /xmlrpc.php
        path *.sql
        path /wp-content/uploads/*.php
    }
    respond @disallowed 404

    encode gzip
}

Praktische Beispiele

Node.js App mit Static Files

app.example.com {
    # Statische Dateien
    handle /static/* {
        root * /var/www/app
        file_server
    }

    # API
    handle /api/* {
        reverse_proxy localhost:3000
    }

    # Frontend
    handle {
        root * /var/www/app/dist
        try_files {path} /index.html
        file_server
    }

    encode gzip
}

Multi-Tenant Setup

*.example.com {
    @tenant1 host app1.example.com
    handle @tenant1 {
        reverse_proxy localhost:3001
    }

    @tenant2 host app2.example.com
    handle @tenant2 {
        reverse_proxy localhost:3002
    }

    handle {
        respond "Unknown tenant" 404
    }
}

API Gateway

api.example.com {
    # Auth Service
    handle /auth/* {
        reverse_proxy auth:3000
    }

    # User Service
    handle /users/* {
        reverse_proxy users:3000
    }

    # Order Service
    handle /orders/* {
        reverse_proxy orders:3000
    }

    # Rate Limiting
    rate_limit {remote.ip} 100r/m

    # Headers
    header {
        Access-Control-Allow-Origin "*"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
    }
}

JSON-Konfiguration

API-basierte Konfiguration

# Konfiguration abrufen
curl localhost:2019/config/

# Konfiguration setzen
curl localhost:2019/config/ -X POST -H "Content-Type: application/json" -d @config.json

JSON-Format

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":443"],
          "routes": [{
            "match": [{"host": ["example.com"]}],
            "handle": [{
              "handler": "reverse_proxy",
              "upstreams": [{"dial": "localhost:3000"}]
            }]
          }]
        }
      }
    }
  }
}

Troubleshooting

Logs prüfen

journalctl -u caddy -f

# Oder mit Debug
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile

Konfiguration testen

caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile

Zertifikat-Probleme

# Zertifikate befinden sich in
ls ~/.local/share/caddy/certificates/

# Manuelles Zertifikat anfordern
caddy trust

Zusammenfassung

| Direktive | Funktion | |-----------|----------| | reverse_proxy | Proxy zu Backend | | file_server | Statische Dateien | | root | Dokumentenwurzel | | encode | Komprimierung | | tls | SSL-Konfiguration | | header | HTTP-Header | | basicauth | Authentifizierung | | log | Logging |

| Matcher | Beispiel | |---------|----------| | @name path /api/ | Pfad-Match | | @name host example.com | Host-Match | | @name remote_ip 10.0.0.0/8 | IP-Match | | @name header Accept json* | Header-Match |

| Befehl | Funktion | |--------|----------| | caddy run | Vordergrund starten | | caddy start | Hintergrund starten | | caddy reload | Konfiguration neu laden | | caddy validate | Konfiguration prüfen |

Fazit

Caddy ist ideal wenn automatisches HTTPS und einfache Konfiguration wichtig sind. Die Caddyfile-Syntax ist intuitiv und deutlich kürzer als Nginx-Konfigurationen. Für die meisten Anwendungsfälle reicht die Standard-Installation ohne zusätzliche Module. Die automatische Zertifikatsverwaltung spart erheblichen Aufwand. Für sehr hohe Performance-Anforderungen bleibt Nginx die bessere Wahl.