NoctisNode
← GuidesNetzwerk2026-10-239 Min.

Caddy als TCP-Reverse-Proxy und SSH-Proxy mit caddy-l4

Das Standard-Caddy leitet nur HTTP weiter. Für reines TCP und SSH bauen Sie Caddy mit der layer4-App caddy-l4, leiten Ports weiter oder teilen Port 443 nach Protokoll zwischen SSH und HTTPS.

Kurz gesagt: Das Standard-Caddy kann kein reines TCP oder SSH weiterleiten, weil sein `reverse_proxy` nur HTTP spricht. Um TCP weiterzuleiten, bauen Sie Caddy mit xcaddy und dem Modul caddy-l4 (github.com/mholt/caddy-l4) und konfigurieren dann dessen `layer4`-App so, dass sie auf einem Port lauscht und Verbindungen weiterleitet — optional mit Wahl des Upstreams nach Protokoll, sodass sich SSH und HTTPS Port 443 teilen können.

caddy-l4 ist ein separates Modul, nicht Teil der Standard-Distribution von Caddy, und seine README beschreibt es weiterhin als Work in Progress, dessen Konfiguration sich zwischen Versionen ändern kann. Diese Anleitung nutzt JSON-Konfiguration, die direkt der dokumentierten Struktur des Moduls entspricht. Wenn Sie nur HTTP- oder HTTPS-Proxying brauchen, genügt die Anleitung zu Caddy als Reverse Proxy.

Warum das Standard-Caddy nur HTTP kann

Die eingebauten Apps von Caddy sind `http`, `tls` und `pki`. Der Handler `reverse_proxy` lebt innerhalb der HTTP-App und braucht daher eine geparste HTTP-Anfrage, bevor er irgendetwas routen kann. Ein SSH-Client, ein PostgreSQL-Client oder ein Spielprotokoll sendet nie eine, also würde Caddy mit einem TLS- oder HTTP-Fehler antworten.

caddy-l4 fügt eine separate `layer4`-App hinzu, die auf rohen Verbindungen arbeitet. Sie kann die ersten Bytes einer Verbindung erkennen (SSH-Banner, TLS-ClientHello mit SNI, HTTP-Request-Zeile, PostgreSQL-Startup und mehr) und dann weiterleiten, TLS terminieren oder den Stream an einen anderen Handler übergeben. Da sie eine App wie `http` ist, können beide im selben Prozess laufen.

BedarfStandard-CaddyCaddy + caddy-l4
HTTPS-Reverse-Proxy mit Auto-TLSJaJa
Einfaches TCP-Port-ForwardingNeinJa (Handler `proxy`)
UDP-ForwardingNeinJa (Lauschen auf `udp/:port`)
TLS-Passthrough nach SNINeinJa (Matcher `tls` mit `sni`)
SSH und HTTPS auf demselben PortNeinJa (Matcher `ssh` und `tls`)
Caddyfile-UnterstützungVollständigTeilweise; JSON ist das Referenzformat

Caddy mit dem layer4-Modul bauen

xcaddy kompiliert Caddy mit zusätzlichen Modulen. Alternativ wählen Sie `mholt/caddy-l4` auf der offiziellen Download-Seite von Caddy und laden ein fertiges Binary herunter. In beiden Fällen prüfen Sie mit `list-modules`, dass `layer4`-Module aufgeführt sind, bevor Sie die Konfiguration schreiben.

# Current Caddy needs a recent Go toolchain; distro golang-go packages are
# often too old, so install the latest release from https://go.dev/dl/
# and make sure /usr/local/go/bin is in PATH, then:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# Build Caddy with the layer4 app
~/go/bin/xcaddy build --with github.com/mholt/caddy-l4

# Confirm the modules are present
./caddy list-modules | grep layer4

Das Paket-Binary sicher ersetzen

Wurde Caddy aus dem offiziellen apt-Repository installiert, ist der dokumentierte Weg, einen eigenen Build einzusetzen, `dpkg-divert` plus `update-alternatives`, damit Paket-Upgrades Ihr Binary nicht überschreiben. Denken Sie daran, dass Sie für Caddy-Updates nun selbst neu bauen müssen.

sudo dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
sudo mv ./caddy /usr/bin/caddy.custom
sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50
sudo systemctl restart caddy

Beispiel 1: einfacher TCP-Reverse-Proxy

Diese Konfiguration lauscht auf Port 5432 und leitet jede Verbindung an einen PostgreSQL-Server unter einer privaten Adresse weiter, etwa am anderen Ende eines WireGuard-Tunnels. Ohne Matcher nimmt jede Verbindung diese Route. Upstreams akzeptieren mehrere `dial`-Adressen für Failover, und der Handler `proxy` unterstützt Load Balancing und Health Checks ähnlich wie `reverse_proxy` bei HTTP.

Das Backend sieht die IP von Caddy, nicht die des Clients. Versteht das Backend das PROXY-Protokoll, setzen Sie `"proxy_protocol": "v2"` am Proxy-Handler, um die ursprüngliche Adresse weiterzugeben.

{
  "apps": {
    "layer4": {
      "servers": {
        "postgres": {
          "listen": [":5432"],
          "routes": [
            {
              "handle": [
                {
                  "handler": "proxy",
                  "upstreams": [{"dial": ["10.8.0.2:5432"]}]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Beispiel 2: SSH und HTTPS gemultiplext auf Port 443

SSH-Clients senden ihr Versionsbanner (`SSH-2.0-...`), sobald sie sich verbinden, sodass caddy-l4 SSH vor jeder Serverantwort erkennen kann. Die Konfiguration unten schickt SSH an den lokalen sshd, TLS für `git.example.com` an einen Dienst, der sein eigenes TLS terminiert, und alles andere TLS an die HTTP-App von Caddy, die auf 8443 lauscht. Da layer4 nun Port 443 belegt, darf die HTTP-App ihn nicht ebenfalls binden: Setzen Sie `https_port 8443` in den globalen Optionen des Caddyfile oder die entsprechende `listen`-Adresse in JSON, und halten Sie Port 80 für HTTP-01-Challenges offen. Mit `https_port 8443` würde die automatische HTTP-zu-HTTPS-Umleitung von Caddy auf Port 8443 zeigen, also setzen Sie `auto_https disable_redirects` und fügen eine eigene Site `http:// { redir https://{host}{uri} permanent }` hinzu.

Protokolle, bei denen der Server zuerst spricht, wie SMTP, FTP oder MySQL, lassen sich so nicht erkennen, weil der Client stumm wartet. Geben Sie ihnen einen eigenen Port.

{
  "apps": {
    "layer4": {
      "servers": {
        "mux443": {
          "listen": [":443"],
          "routes": [
            {
              "match": [{"ssh": {}}],
              "handle": [
                {"handler": "proxy", "upstreams": [{"dial": ["127.0.0.1:22"]}]}
              ]
            },
            {
              "match": [{"tls": {"sni": ["git.example.com"]}}],
              "handle": [
                {"handler": "proxy", "upstreams": [{"dial": ["127.0.0.1:3443"]}]}
              ]
            },
            {
              "match": [{"tls": {}}],
              "handle": [
                {"handler": "proxy", "upstreams": [{"dial": ["127.0.0.1:8443"]}]}
              ]
            }
          ]
        }
      }
    }
  }
}

Starten und testen

  1. 01.Speichern Sie das JSON als `/etc/caddy/caddy.json` und validieren Sie es.
  2. 02.Stoppen Sie den Dienst und starten Sie Caddy im Vordergrund, um beim Verbinden das Log zu beobachten.
  3. 03.Lassen Sie die systemd-Unit per Drop-in-Override auf die JSON-Datei zeigen, dann `sudo systemctl daemon-reload` und `sudo systemctl start caddy`.
  4. 04.Verbinden Sie sich mit `ssh -p 443` und öffnen Sie die HTTPS-Site im Browser, um zu bestätigen, dass beide Routen funktionieren.
# Validate and test in the foreground first
sudo caddy validate --config /etc/caddy/caddy.json
sudo systemctl stop caddy
sudo caddy run --config /etc/caddy/caddy.json

# Make the service use the JSON file: sudo systemctl edit caddy
[Service]
ExecStart=
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/caddy.json
ExecReload=
ExecReload=/usr/bin/caddy reload --config /etc/caddy/caddy.json --force

# Client side
ssh -p 443 [email protected]

Wann stattdessen HAProxy oder sslh

caddy-l4 passt gut, wenn Sie bereits Caddy betreiben und einen Prozess für HTTPS und ein paar TCP-Routen wollen. Für ein TCP-lastiges Setup sind ausgereifte Alternativen aus den Debian- und Ubuntu-Repositories womöglich einfacher zu betreiben: HAProxy im `mode tcp` routet per `req.ssl_sni` nach SNI und bietet detaillierte Statistiken und Health Checks, während sslh ein kleiner Daemon ist, der nur dafür gebaut wurde, einen Port zwischen SSH, TLS, OpenVPN und anderen zu teilen. Beide werden per apt installiert und mit dem restlichen System aktualisiert — ohne eigenen Build, den man pflegen muss.

SSH auf 443 hilft in Netzen, die nur Web-Ports erlauben, ist aber kein Sicherheitsfeature. Behalten Sie die reine Schlüssel-Authentifizierung aus der Anleitung zur SSH-Härtung bei. Auf einem NoctisNode-VPS haben Sie vollen Root-Zugriff, sodass jedes dieser Tools die benötigten Ports binden kann; zum Zertifikatsverhalten auf der HTTPS-Seite siehe Caddy Automatic HTTPS und für Container Caddy als Docker-Reverse-Proxy.

ToolStärkenNachteile
Caddy + caddy-l4Ein Prozess für automatisches HTTPS und TCP, Protokoll-Matcher, JSON-APIEigener Build, Modul noch in Entwicklung
HAProxyAusgereifter TCP/HTTP-Proxy, SNI-Routing, umfangreiche Health Checks und StatistikenMehr Konfiguration, kein eingebauter ACME-Client
sslhWinzig, speziell für Port-Sharing von SSH/TLS/VPN gebautNur Multiplexing; keine TLS-Terminierung, kein Load Balancing

FAQ

QKann Caddy TCP-Traffic weiterleiten?

ANicht von Haus aus. Das Standard-Caddy leitet nur HTTP weiter; für reines TCP oder UDP brauchen Sie einen Caddy-Build mit dem Drittanbieter-Modul caddy-l4 (layer4).

QWie leite ich SSH über Caddy weiter?

ABauen Sie Caddy mit caddy-l4, fügen Sie einen layer4-Server hinzu, der auf `ssh` matcht und an `127.0.0.1:22` weiterleitet, und verbinden Sie sich dann mit `ssh -p <port>`. Auf Port 443 können Sie das mit einer `tls`-Route kombinieren, damit HTTPS weiter funktioniert.

QKönnen sich SSH und HTTPS Port 443 teilen?

AJa. caddy-l4, sslh und HAProxy können die ersten Bytes einer Verbindung prüfen und SSH und TLS auf demselben Port an unterschiedliche Backends routen.

QUnterstützt caddy-l4 das Caddyfile?

AEs bietet Caddyfile-Unterstützung über eine globale Option `layer4`, aber JSON bleibt das vollständigste und am besten dokumentierte Format. Prüfen Sie die README des Moduls für die Version, die Sie bauen.

Caddy als TCP-Reverse-Proxy und SSH-Proxy mit caddy-l4 · NoctisNode