NoctisNode
← GuíasRedes2026-10-239 min

Reverse proxy TCP y proxy SSH con Caddy y caddy-l4

Caddy de serie solo hace proxy de HTTP. Para TCP en bruto y SSH, compila Caddy con la app layer4 de caddy-l4, reenvía puertos o comparte el puerto 443 entre SSH y HTTPS según el protocolo.

Respuesta corta: Caddy estándar no puede hacer proxy de TCP en bruto ni de SSH, porque su `reverse_proxy` solo habla HTTP. Para reenviar TCP, compila Caddy con el módulo caddy-l4 (github.com/mholt/caddy-l4) usando xcaddy y configura su app `layer4` para escuchar en un puerto y reenviar las conexiones, eligiendo opcionalmente el upstream según el protocolo, de modo que SSH y HTTPS puedan compartir el puerto 443.

caddy-l4 es un módulo aparte, no forma parte de la distribución por defecto de Caddy, y su README todavía lo describe como un trabajo en curso cuya configuración puede cambiar entre versiones. Esta guía usa configuración JSON, que se corresponde directamente con la estructura documentada del módulo. Si solo necesitas proxy HTTP o HTTPS, la guía de reverse proxy con Caddy es todo lo que te hace falta.

Por qué Caddy de serie solo maneja HTTP

Las apps integradas de Caddy son `http`, `tls` y `pki`. El handler `reverse_proxy` vive dentro de la app HTTP, así que necesita una petición HTTP ya analizada antes de poder enrutar nada. Un cliente SSH, un cliente de PostgreSQL o un protocolo de juego nunca la envían, por lo que Caddy respondería con un error de TLS o HTTP.

caddy-l4 añade una app `layer4` independiente que trabaja con conexiones en bruto. Puede reconocer los primeros bytes de una conexión (banner SSH, ClientHello de TLS con SNI, línea de petición HTTP, arranque de PostgreSQL y más) y después hacer proxy, terminar TLS o pasar el flujo a otro handler. Como es una app igual que `http`, ambas pueden ejecutarse en el mismo proceso.

NecesidadCaddy de serieCaddy + caddy-l4
Reverse proxy HTTPS con TLS automáticoSíSí
Reenvío de puertos TCP simpleNoSí (handler `proxy`)
Reenvío UDPNoSí (escuchando en `udp/:port`)
TLS passthrough por SNINoSí (matcher `tls` con `sni`)
SSH y HTTPS en el mismo puertoNoSí (matchers `ssh` y `tls`)
Soporte de CaddyfileCompletoParcial; JSON es el formato de referencia

Compila Caddy con el módulo layer4

xcaddy compila Caddy con módulos adicionales. Como alternativa, selecciona `mholt/caddy-l4` en la página oficial de descargas de Caddy y descarga un binario listo. En ambos casos, comprueba con `list-modules` que aparecen los módulos `layer4` antes de escribir la configuración.

# 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

Sustituye el binario del paquete de forma segura

Si Caddy se instaló desde el repositorio apt oficial, la forma documentada de poner una build personalizada es `dpkg-divert` más `update-alternatives`, para que las actualizaciones del paquete no sobrescriban tu binario. Recuerda que a partir de ahora tendrás que recompilar para recibir las actualizaciones de Caddy.

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

Ejemplo 1: reverse proxy TCP simple

Esta configuración escucha en el puerto 5432 y reenvía cada conexión a un servidor PostgreSQL en una dirección privada, como el otro extremo de un túnel WireGuard. Sin matcher, todas las conexiones siguen la ruta. Los upstreams aceptan varias direcciones `dial` para failover, y el handler `proxy` admite balanceo de carga y health checks parecidos a los de `reverse_proxy` en HTTP.

El backend ve la IP de Caddy, no la del cliente. Si el backend entiende el protocolo PROXY, define `"proxy_protocol": "v2"` en el handler proxy para pasar la dirección original.

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

Ejemplo 2: SSH y HTTPS multiplexados en el puerto 443

Los clientes SSH envían su banner de versión (`SSH-2.0-...`) nada más conectarse, así que caddy-l4 puede reconocer SSH antes de cualquier respuesta del servidor. La configuración de abajo envía SSH al sshd local, el TLS de `git.example.com` a un servicio que termina su propio TLS y el resto del TLS a la app HTTP de Caddy escuchando en el 8443. Como layer4 ocupa ahora el puerto 443, la app HTTP no debe escuchar también en él: define `https_port 8443` en las opciones globales del Caddyfile, o la dirección `listen` equivalente en JSON, y mantén abierto el puerto 80 para los desafíos HTTP-01. Con `https_port 8443`, la redirección automática de HTTP a HTTPS de Caddy apuntaría al puerto 8443, así que define `auto_https disable_redirects` y añade tu propio sitio `http:// { redir https://{host}{uri} permanent }`.

Los protocolos en los que habla primero el servidor, como SMTP, FTP o MySQL, no pueden detectarse así, porque el cliente espera en silencio. Dales su propio puerto.

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

Ejecútalo y pruébalo

  1. 01.Guarda el JSON como `/etc/caddy/caddy.json` y valídalo.
  2. 02.Detén el servicio y ejecuta Caddy en primer plano para ver el log mientras te conectas.
  3. 03.Apunta la unidad de systemd al archivo JSON con un override drop-in y luego ejecuta `sudo systemctl daemon-reload` y `sudo systemctl start caddy`.
  4. 04.Conéctate con `ssh -p 443` y abre el sitio HTTPS en un navegador para confirmar que ambas rutas funcionan.
# 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]

Cuándo usar HAProxy o sslh en su lugar

caddy-l4 encaja bien cuando ya usas Caddy y quieres un único proceso para HTTPS y unas pocas rutas TCP. Para una configuración con mucho TCP, alternativas maduras de los repositorios de Debian y Ubuntu pueden ser más fáciles de operar: HAProxy en `mode tcp` enruta por SNI con `req.ssl_sni` y tiene estadísticas y health checks detallados, mientras que sslh es un pequeño daemon creado solo para compartir un puerto entre SSH, TLS, OpenVPN y otros. Ambos se instalan con apt y se actualizan con el resto del sistema, sin builds personalizadas que mantener.

Exponer SSH en el 443 ayuda en redes que solo permiten puertos web, pero no es una medida de seguridad. Mantén la autenticación solo con clave de la guía de hardening de SSH. En un VPS de NoctisNode tienes root completo, así que cualquiera de estas herramientas puede escuchar en los puertos que necesites; para el comportamiento de los certificados en la parte HTTPS consulta HTTPS automático en Caddy y, para contenedores, Caddy como reverse proxy de Docker.

HerramientaPuntos fuertesContrapartidas
Caddy + caddy-l4Un solo proceso para HTTPS automático y TCP, matchers de protocolo, API JSONBuild personalizada, módulo todavía en evolución
HAProxyProxy TCP/HTTP maduro, enrutado por SNI, health checks y estadísticas completosMás configuración, sin cliente ACME integrado
sslhDiminuto, hecho a medida para compartir puertos entre SSH/TLS/VPNSolo multiplexa; sin terminación TLS ni balanceo de carga

Preguntas frecuentes

Q¿Puede Caddy hacer proxy de tráfico TCP?

ANo de serie. Caddy estándar solo hace proxy de HTTP; para TCP o UDP en bruto necesitas una build de Caddy que incluya el módulo de terceros caddy-l4 (layer4).

Q¿Cómo hago proxy de SSH a través de Caddy?

ACompila Caddy con caddy-l4, añade un servidor layer4 que reconozca `ssh` y reenvíe a `127.0.0.1:22`, y conéctate con `ssh -p <port>`. En el puerto 443 puedes combinarlo con una ruta `tls` para que HTTPS siga funcionando.

Q¿Pueden SSH y HTTPS compartir el puerto 443?

ASí. caddy-l4, sslh y HAProxy pueden inspeccionar los primeros bytes de una conexión y enrutar SSH y TLS a backends distintos en el mismo puerto.

Q¿caddy-l4 admite el Caddyfile?

ATiene soporte de Caddyfile mediante una opción global `layer4`, pero JSON sigue siendo el formato más completo y mejor documentado. Consulta el README del módulo para la versión que compiles.

Reverse proxy TCP y proxy SSH con Caddy y caddy-l4 · NoctisNode