NoctisNode
← HandleidingenNetwerken2026-10-239 min

Caddy als TCP-reverse proxy en SSH-proxy met caddy-l4

Standaard proxyt Caddy alleen HTTP. Voor ruwe TCP en SSH bouw je Caddy met de layer4-app caddy-l4, stuur je poorten door of deel je poort 443 tussen SSH en HTTPS op basis van protocol.

Kort gezegd: standaard Caddy kan geen ruwe TCP of SSH proxyen, omdat `reverse_proxy` alleen HTTP spreekt. Om TCP door te sturen bouw je Caddy met de module caddy-l4 (github.com/mholt/caddy-l4) via xcaddy, en configureer je daarna de `layer4`-app om op een poort te luisteren en verbindingen te proxyen, eventueel met de upstream gekozen op basis van protocol, zodat SSH en HTTPS poort 443 kunnen delen.

caddy-l4 is een losse module, geen onderdeel van de standaarddistributie van Caddy, en de README beschrijft hem nog steeds als werk in uitvoering waarvan de config tussen versies kan veranderen. Deze gids gebruikt JSON-config, die direct overeenkomt met de gedocumenteerde structuur van de module. Heb je alleen HTTP- of HTTPS-proxying nodig, dan is de handleiding voor Caddy als reverse proxy alles wat je nodig hebt.

Waarom standaard Caddy alleen HTTP doet

De ingebouwde apps van Caddy zijn `http`, `tls` en `pki`. De handler `reverse_proxy` zit in de HTTP-app, dus hij heeft een geparst HTTP-verzoek nodig voordat hij iets kan routeren. Een SSH-client, een PostgreSQL-client of een gameprotocol stuurt dat nooit, dus Caddy zou antwoorden met een TLS- of HTTP-fout.

caddy-l4 voegt een aparte `layer4`-app toe die op ruwe verbindingen werkt. Die kan de eerste bytes van een verbinding herkennen (SSH-banner, TLS-ClientHello met SNI, HTTP-requestregel, PostgreSQL-startup en meer) en dan proxyen, TLS afhandelen of de stream aan een andere handler doorgeven. Omdat het een app is zoals `http`, kunnen beide in hetzelfde proces draaien.

BehoefteStandaard CaddyCaddy + caddy-l4
HTTPS-reverse proxy met auto-TLSJaJa
Gewone TCP-port forwardingNeeJa (`proxy`-handler)
UDP doorsturenNeeJa (luisteren op `udp/:port`)
TLS-passthrough op basis van SNINeeJa (`tls`-matcher met `sni`)
SSH en HTTPS op dezelfde poortNeeJa (`ssh`- en `tls`-matchers)
Ondersteuning voor CaddyfileVolledigGedeeltelijk; JSON is het referentieformaat

Caddy bouwen met de layer4-module

xcaddy compileert Caddy met extra modules. Je kunt ook `mholt/caddy-l4` selecteren op de officiële downloadpagina van Caddy en een kant-en-klare binary downloaden. Hoe dan ook: controleer met `list-modules` dat de `layer4`-modules erin staan voordat je config schrijft.

# 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

De binary uit het pakket veilig vervangen

Is Caddy geïnstalleerd vanuit de officiële apt-repository, dan is de gedocumenteerde manier om een eigen build in te zetten `dpkg-divert` plus `update-alternatives`, zodat pakketupgrades je binary niet overschrijven. Onthoud dat je nu zelf opnieuw moet bouwen om Caddy-updates te krijgen.

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

Voorbeeld 1: gewone TCP-reverse proxy

Deze config luistert op poort 5432 en stuurt elke verbinding door naar een PostgreSQL-server op een privéadres, zoals de andere kant van een WireGuard-tunnel. Geen matcher betekent dat elke verbinding deze route neemt. Upstreams accepteren meerdere `dial`-adressen voor failover, en de `proxy`-handler ondersteunt load balancing en health checks, vergelijkbaar met HTTP-`reverse_proxy`.

De backend ziet het IP van Caddy, niet dat van de client. Begrijpt de backend het PROXY-protocol, zet dan `"proxy_protocol": "v2"` op de proxy-handler om het oorspronkelijke adres door te geven.

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

Voorbeeld 2: SSH en HTTPS gemultiplext op poort 443

SSH-clients sturen hun versiebanner (`SSH-2.0-...`) zodra ze verbinden, dus caddy-l4 kan SSH herkennen nog vóór er een antwoord van de server is. De config hieronder stuurt SSH naar de lokale sshd, TLS voor `git.example.com` naar een dienst die zijn eigen TLS afhandelt, en alle overige TLS naar de HTTP-app van Caddy die op 8443 luistert. Omdat layer4 nu poort 443 bezet, mag de HTTP-app daar niet ook aan binden: zet `https_port 8443` in de globale opties van de Caddyfile, of het equivalente `listen`-adres in JSON, en houd poort 80 open voor HTTP-01-challenges. Met `https_port 8443` zou de automatische doorverwijzing van HTTP naar HTTPS naar poort 8443 wijzen, dus zet `auto_https disable_redirects` en voeg je eigen site `http:// { redir https://{host}{uri} permanent }` toe.

Protocollen waarbij de server als eerste spreekt, zoals SMTP, FTP of MySQL, kun je zo niet herkennen, omdat de client stil wacht. Geef die een eigen poort.

{
  "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"]}]}
              ]
            }
          ]
        }
      }
    }
  }
}

Draaien en testen

  1. 01.Sla de JSON op als `/etc/caddy/caddy.json` en valideer hem.
  2. 02.Stop de dienst en draai Caddy op de voorgrond om de log te volgen terwijl je verbindt.
  3. 03.Laat de systemd-unit met een drop-in-override naar het JSON-bestand wijzen, en voer dan `sudo systemctl daemon-reload` en `sudo systemctl start caddy` uit.
  4. 04.Verbind met `ssh -p 443` en open de HTTPS-site in een browser om te bevestigen dat beide routes werken.
# 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]

Wanneer je beter HAProxy of sslh gebruikt

caddy-l4 past goed als je Caddy al draait en één proces wilt voor HTTPS en een paar TCP-routes. Voor een opzet met veel TCP zijn volwassen alternatieven uit de repository's van Debian en Ubuntu mogelijk eenvoudiger te beheren: HAProxy in `mode tcp` routeert op SNI met `req.ssl_sni` en heeft uitgebreide statistieken en health checks, terwijl sslh een kleine daemon is die alleen gebouwd is om één poort te delen tussen SSH, TLS, OpenVPN en andere. Beide installeer je met apt en worden met de rest van het systeem bijgewerkt, zonder eigen build om te onderhouden.

SSH op 443 aanbieden helpt op netwerken die alleen webpoorten toestaan, maar het is geen beveiligingsmaatregel. Houd authenticatie alleen met sleutels aan uit de handleiding voor SSH-hardening. Op een NoctisNode-VPS heb je volledige root, dus elk van deze tools kan de poorten binden die je nodig hebt; voor het gedrag van certificaten aan de HTTPS-kant zie automatische HTTPS in Caddy en, voor containers, Caddy als Docker-reverse proxy.

ToolSterke puntenNadelen
Caddy + caddy-l4Eén proces voor auto-HTTPS en TCP, protocolmatchers, JSON-APIEigen build, module nog in ontwikkeling
HAProxyVolwassen TCP/HTTP-proxy, SNI-routering, uitgebreide health checks en statistiekenMeer config, geen ingebouwde ACME-client
sslhPiepklein, speciaal gebouwd om poorten te delen voor SSH/TLS/VPNAlleen multiplexen; geen TLS-afhandeling of load balancing

Veelgestelde vragen

QKan Caddy TCP-verkeer proxyen?

ANiet standaard. Caddy proxyt van zichzelf alleen HTTP; voor ruwe TCP of UDP heb je een Caddy-build nodig met de externe module caddy-l4 (layer4).

QHoe proxy ik SSH via Caddy?

ABouw Caddy met caddy-l4, voeg een layer4-server toe die op `ssh` matcht en naar `127.0.0.1:22` proxyt, en verbind dan met `ssh -p <port>`. Op poort 443 kun je dat combineren met een `tls`-route zodat HTTPS blijft werken.

QKunnen SSH en HTTPS poort 443 delen?

AJa. caddy-l4, sslh en HAProxy kunnen allemaal de eerste bytes van een verbinding inspecteren en SSH en TLS op dezelfde poort naar verschillende backends routeren.

QOndersteunt caddy-l4 de Caddyfile?

AHet heeft ondersteuning voor de Caddyfile via een globale optie `layer4`, maar JSON blijft het meest complete en best gedocumenteerde formaat. Raadpleeg de README van de module voor de versie die je bouwt.

Caddy als TCP-reverse proxy en SSH-proxy met caddy-l4 · NoctisNode