TL;DR — Kurzzusammenfassung

Caddy als Reverse Proxy mit automatischem HTTPS. Caddyfile-Syntax, Load-Balancing-Richtlinien, Health Checks und Produktions-Caddyfile für mehrere Sites.

Caddy ist ein moderner Open-Source-Webserver, geschrieben in Go, dessen definierendes Merkmal automatisches HTTPS ist: Er provisoniert und erneuert TLS-Zertifikate von Let’s Encrypt oder ZeroSSL in dem Moment, in dem Sie eine Domain darauf zeigen — ganz ohne manuelle Certbot-Einrichtung. Als Reverse Proxy kombiniert Caddy dieses konfigurationsfreie TLS-Management mit einer sauberen deklarativen Syntax, integriertem Load Balancing, standardmäßig aktiviertem HTTP/2 und HTTP/3, und einem einzigen statisch kompilierten Binary ohne externe Abhängigkeiten. Diese Anleitung deckt alles ab, was Sie für den Betrieb von Caddy als Produktions-Reverse-Proxy benötigen: Installation, Caddyfile-Syntax, alle Load-Balancing-Richtlinien, aktive und passive Health Checks, Header-Manipulation, Authentifizierung, On-Demand-TLS, die Admin-API und eine vollständige Caddyfile für Multi-Site-Hosting.

Warum Caddy als Reverse Proxy wählen?

Hier ist ein direkter Vergleich der wichtigsten Reverse-Proxy-Optionen, die 2026 verfügbar sind:

FunktionCaddyNginxTraefikHAProxyApache
Automatisches HTTPSIntegrierter ACME-ClientManuell (Certbot)Integriert (via ACME)ManuellManuell (mod_md)
KonfigurationsformatCaddyfile (minimal)nginx.conf (ausführlich)YAML/TOML/Docker-Labelshaproxy.cfghttpd.conf
HTTP/2StandardmäßigExplizite KonfigurationStandardmäßigNein (nur TCP)Explizite Konfiguration
HTTP/3 (QUIC)StandardmäßigExperimentellVia PluginNeinNein
Einzelnes BinaryJa (Go, keine Deps)Nein (C, mit Modulen)Ja (Go)Ja (C)Nein
Konfigurations-APIVollständige REST-APINeinVollständige REST-APINur Statistik-SocketNein
Load Balancing8 integrierte RichtlinienBegrenzt integriertMehrere AnbieterAusgezeichnetGrundlegend
Speicherbedarf~20-50 MB~5-15 MB~25-60 MB~5-10 MB~30-80 MB
LernkurveNiedrigMittel-HochMittelHochMittel-Hoch

Voraussetzungen

  • Ein Linux-Server mit Ubuntu 22.04+, Debian 12+ oder einer RHEL/CentOS-kompatiblen Distribution
  • Ein Domainname mit DNS A/AAAA-Einträgen, die auf die öffentliche IP des Servers zeigen
  • Ports 80 und 443 in Ihrer Firewall geöffnet
  • Root- oder sudo-Zugang
  • Kein anderer Prozess, der an den Ports 80 oder 443 gebunden ist
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw status

Caddy installieren

Ubuntu und Debian (APT)

sudo apt update && sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

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 -y caddy
caddy version

RHEL, CentOS, Fedora (DNF)

dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy

Docker

docker run -d \
  -p 80:80 -p 443:443 -p 443:443/udp \
  -v /pfad/zur/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  -v caddy_config:/config \
  caddy:latest

xcaddy — Benutzerdefinierte Builds mit Plugins

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

xcaddy build \
  --with github.com/caddy-dns/cloudflare \
  --with github.com/mholt/caddy-ratelimit

sudo mv caddy /usr/bin/caddy && sudo systemctl restart caddy

Caddyfile-Syntax

Struktur

# Globaler Optionsblock
{
    email admin@beispiel.de
    admin off
    grace_period 10s
}

# Site-Block — die Domain löst automatisches HTTPS aus
app.beispiel.de {
    direktive argument
}

Matcher

beispiel.de {
    @api path /api/*
    reverse_proxy @api localhost:8000

    reverse_proxy /statisch/* localhost:9000

    file_server *
}

Platzhalter (Placeholders)

beispiel.de {
    reverse_proxy localhost:3000 {
        header_up X-Echte-IP     {remote_host}
        header_up X-Weitergeleiteter-Port {server_port}
        header_up X-Anfrage-ID   {uuid}
    }
}

Reverse-Proxy-Konfiguration

Einfaches einzelnes Backend

app.beispiel.de {
    reverse_proxy localhost:3000
}

Diese einzelne Zeile bietet: automatisches HTTPS, HTTP/2, HTTP/3, HTTP→HTTPS-Weiterleitung, X-Forwarded-For-Header-Weitergabe und transparente WebSocket-Unterstützung.

Pfadbasiertes Routing

beispiel.de {
    reverse_proxy /api/*    localhost:8000
    reverse_proxy /ws/*     localhost:4000
    reverse_proxy /admin/*  localhost:9000

    root * /var/www/frontend
    file_server
}

Header-Manipulation

app.beispiel.de {
    reverse_proxy localhost:3000 {
        header_up Host              {upstream_hostport}
        header_up X-Echte-IP        {remote_host}
        header_up X-Weitergeleitetes-Proto {scheme}

        header_up -Authorization

        header_down -X-Interne-Server-ID
        header_down Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    }
}

WebSocket und langlaufende Verbindungen

ws.beispiel.de {
    reverse_proxy localhost:4000 {
        flush_interval -1
    }
}

Load Balancing

Mehrere Backends

app.beispiel.de {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003
}

Alle Load-Balancing-Richtlinien

app.beispiel.de {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy least_conn         # Backend mit wenigsten aktiven Verbindungen
        # lb_policy round_robin      # Sequentielle Rotation (Standard)
        # lb_policy first            # Immer das erste verfügbare Backend
        # lb_policy random           # Zufällige Auswahl
        # lb_policy ip_hash          # Client-IP → konsistentes Backend
        # lb_policy cookie caddy_lb  # Cookie-basierte Sitzungsbeibehaltung
        # lb_policy header X-Shard   # Routing nach Header-Wert
        # lb_policy uri_hash         # Anfrage-URI → konsistentes Backend
    }
}

Aktive Health Checks

app.beispiel.de {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy least_conn

        health_uri      /health
        health_interval 15s
        health_timeout  5s
        health_status   200
    }
}

Passive Health Checks

app.beispiel.de {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy round_robin

        fail_duration     30s
        max_fails         3
        unhealthy_latency 500ms
        unhealthy_status  5xx
    }
}

Automatisches HTTPS im Detail

ACME-Challenge-Typen

{
    email admin@beispiel.de
}

# HTTP-01 (Standard)
beispiel.de {
    reverse_proxy localhost:3000
}

# TLS-ALPN-01: Verwendet Port 443
nur-tls.beispiel.de {
    tls {
        challenges tls-alpn-01
    }
    reverse_proxy localhost:3001
}

# DNS-01: Keine eingehenden Ports — benötigt xcaddy + DNS-Plugin
wildcard.beispiel.de {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:3002
}

On-Demand-TLS für dynamische Domains

{
    on_demand_tls {
        ask      http://localhost:9001/domain-pruefen
        interval 2m
        burst    5
    }
}

:443 {
    tls {
        on_demand
    }
    reverse_proxy localhost:3000
}

Interne CA für die Entwicklung

{
    local_certs
}

localhost, 127.0.0.1, ::1 {
    reverse_proxy localhost:3000
}

Authentifizierung

HTTP-Basisauthentifizierung

admin.beispiel.de {
    basicauth /admin/* {
        alice $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GT.VVKf3KCrXxpHPkAXmn9sQHO
    }
    reverse_proxy localhost:9000
}
caddy hash-password --plaintext 'ihrpasswort'

Delegierte Authentifizierung (Authelia, oauth2-proxy)

app.beispiel.de {
    forward_auth authelia:9091 {
        uri /api/verify?rd=https://auth.beispiel.de
        copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
    }
    reverse_proxy localhost:3000
}

Dateiserver und SPA

Statischer Dateiserver

statisch.beispiel.de {
    root * /var/www/html
    encode gzip zstd
    file_server {
        hide .git .env .htaccess
    }
}

Single-Page-Application (SPA)

spa.beispiel.de {
    root * /var/www/app/dist
    encode gzip zstd
    try_files {path} /index.html
    file_server
}

Komprimierung und Zugriffsprotokollierung

beispiel.de {
    encode {
        zstd
        gzip 6
        minimum_length 1024
    }

    log {
        output file /var/log/caddy/zugriff.log {
            roll_size    100MiB
            roll_keep    10
            roll_keep_for 720h
        }
        format json
        level  INFO
    }

    reverse_proxy localhost:3000
}

Die Caddy-Admin-API

# Aktuelle Konfiguration anzeigen
curl http://localhost:2019/config/

# Konfiguration aus Caddyfile neu laden
curl -X POST http://localhost:2019/load \
  -H "Content-Type: text/caddyfile" \
  --data-binary @/etc/caddy/Caddyfile

Produktions-Caddyfile für mehrere Sites

{
    email admin@beispiel.de
    grace_period 30s
    admin unix//run/caddy/admin.sock
}

(sicherheits_header) {
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        X-Content-Type-Options    "nosniff"
        X-Frame-Options           "SAMEORIGIN"
        Referrer-Policy           "strict-origin-when-cross-origin"
        -Server
        -X-Powered-By
    }
}

app.beispiel.de {
    import sicherheits_header
    encode zstd gzip

    log {
        output file /var/log/caddy/app-zugriff.log { roll_size 50MiB; roll_keep 7 }
        format json
    }

    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy       least_conn
        health_uri      /health
        health_interval 10s
        health_timeout  3s
        fail_duration   30s
        max_fails       3

        header_up X-Echte-IP          {remote_host}
        header_up X-Weitergeleitetes-Proto {scheme}
        header_down -X-Interner-Trace
    }
}

www.beispiel.de {
    import sicherheits_header
    root * /var/www/frontend/dist
    encode zstd gzip

    @statisch path *.css *.js *.png *.jpg *.webp *.gif *.svg *.woff2 *.ico
    header @statisch Cache-Control "public, max-age=31536000, immutable"

    try_files {path} /index.html
    file_server
}

Fallstricke und Sonderfälle

Port 80 muss für HTTP-01-Challenges erreichbar sein. Wenn Ihr Cloud-Anbieter oder Ihre Firewall Port 80 blockiert, verwenden Sie den tls-alpn-01-Challenge oder DNS-01 via xcaddy mit einem DNS-Plugin.

Let’s Encrypt-Ratenlimits. Der Produktions-ACME-Server erlaubt 5 doppelte Zertifikate pro Domain pro Woche. Setzen Sie während Tests acme_ca https://acme-staging-v02.api.letsencrypt.org/directory.

Der caddy-Benutzer benötigt Lesezugriff auf Ihre Web-Roots. Führen Sie immer sudo chown -R caddy:caddy /var/www/ihresite aus, nachdem Sie Dateien erstellt oder kopiert haben.

Das Flush-Intervall ist wichtig für Streaming. Server-Sent Events (SSE) und Streaming-APIs erfordern flush_interval -1 im reverse_proxy-Block.

Fehlerbehebung

# Caddyfile-Syntax validieren
caddy validate --config /etc/caddy/Caddyfile

# Caddyfile automatisch formatieren
caddy fmt --overwrite /etc/caddy/Caddyfile

# Live-Protokolle verfolgen
sudo journalctl -u caddy -f --no-pager

# Prüfen, auf welchen Ports Caddy lauscht
sudo ss -tlnp | grep caddy

# TLS-Zertifikat prüfen
echo | openssl s_client -connect beispiel.de:443 -servername beispiel.de 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Zusammenfassung

  • Caddy ist ein einzelnes Go-Binary ohne externe Abhängigkeiten, das automatisches HTTPS via ACME, HTTP/2 und HTTP/3 standardmäßig und eine minimale Caddyfile-Syntax bietet
  • Die reverse_proxy-Direktive verwaltet Proxying, Load Balancing, Header-Manipulation, WebSocket-Unterstützung und Health Checks in einem einzigen Block
  • Acht Load-Balancing-Richtlinien decken alle Szenarien ab, von zustandslosem Round-Robin bis zur Cookie-basierten Sitzungsaffinität
  • Aktive und passive Health Checks können für eine robuste Fehlererkennung kombiniert werden
  • On-Demand-TLS ermöglicht die Zertifikatsausstellung pro Tenant für SaaS-Plattformen ohne Vorkonfiguration
  • Verwenden Sie Snippets, um Konfigurationsblöcke zwischen mehreren Site-Definitionen zu teilen

Verwandte Artikel