Por qué un Cloudflare Tunnel en lugar de abrir un puerto
La configuración clásica — abrir los puertos 80 y 443 en el firewall, apuntar un registro DNS a la IP del servidor, instalar un reverse proxy — funciona bien cuando usted controla la red. Pero hay tres situaciones que la hacen fracasar: un proveedor de acceso o una red corporativa que bloquea las conexiones entrantes en el 443, una IP dinámica que invalida sus registros DNS cada 24 horas, o un VPS situado detrás de un NAT estricto que no permite ningún port forwarding.
Cloudflare Tunnel sortea estos tres casos con un mismo mecanismo: el demonio cloudflared establece una conexión saliente persistente hacia los puntos de presencia de Cloudflare. Su servidor no acepta nada, Cloudflare recibe las peticiones HTTPS y las transmite por ese túnel cifrado. El tráfico entrante nunca llega directamente a su servidor.
Lo que aporta Cloudflare Tunnel en la práctica
- Cero puertos abiertos — el firewall del VPS puede bloquear todo el tráfico entrante (80 y 443 incluidos) sin afectar a la accesibilidad de la aplicación.
- HTTPS automático — Cloudflare gestiona el certificado TLS del lado del cliente: nada de Let's Encrypt que configurar, ninguna renovación que vigilar.
- NAT e IP dinámica transparentes — la conexión saliente de
cloudflaredatraviesa cualquier NAT; la IP del servidor puede cambiar sin reconfigurar el DNS. - Red corporativa o proveedor restrictivo — si el puerto 443 entrante está bloqueado en su ubicación, el túnel sigue funcionando porque se apoya en conexiones salientes HTTP/2 o QUIC.
- Integración Zero Trust opcional — los túneles se combinan con Cloudflare Access para restringir el acceso a usuarios autenticados, sin VPN.
- Protección de Cloudflare incluida — el tráfico pasa por la red de Cloudflare: mitigación de DDoS, WAF y rate limiting se aplican sin configuración adicional.
- Plan gratuito disponible — un túnel sencillo, sin load balancing, se puede usar sin ninguna suscripción de pago de Cloudflare.
Requisitos previos
Para seguir esta guía necesita:
Un VPS con acceso root. La instalación de cloudflared como servicio systemd — la única manera de garantizar el reinicio automático — exige permisos de root. Un alojamiento compartido o una instancia sin acceso root no permite esta configuración.
Recursos mínimos. cloudflared consume menos de 50 MB de RAM y un CPU despreciable. Cuente 1 vCPU y 512 MB de RAM como mínimo estricto solo para el demonio; la restricción real viene de la aplicación que va a exponer.
Un dominio gestionado por Cloudflare. El dominio debe estar registrado o transferido a Cloudflare (o con delegación NS hacia Cloudflare). Sin una zona Cloudflare activa, un túnel named no puede crear el registro DNS automático.
Docker Engine (si utiliza la variante Docker Compose de esta guía). Disponible en Ubuntu 22.04/24.04, Debian 12 y las distribuciones compatibles con RHEL.
Una cuenta gratuita de Cloudflare. No se requiere ninguna suscripción de pago para un túnel único sin load balancing.
Instalar y configurar cloudflared en el VPS
Instalar cloudflared desde el repositorio de Cloudflare
Cloudflare publica
cloudflaredcomo paquete.deb/.rpmy como binario estático. Para una instalación con APT en Debian/Ubuntu:curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list sudo apt update && sudo apt install cloudflaredCompruebe que la instalación se ha realizado correctamente:
cloudflared --versionEl comando debe devolver una línea del tipo
cloudflared version 2025.x.x (built ...). La versión exacta depende del momento de la instalación; consulte el repositorio oficial cloudflare/cloudflared para conocer el número actual.Autenticar cloudflared ante Cloudflare
En el VPS (o de forma local si dispone de acceso gráfico), ejecute:
cloudflared tunnel loginEn la terminal aparece un enlace. Ábralo en un navegador, seleccione la zona de Cloudflare que desea autorizar y confirme. Se crea un certificado
~/.cloudflared/cert.pemen la máquina.Crear un túnel con nombre
Cree un túnel con un nombre descriptivo:
cloudflared tunnel create mon-tunnelCloudflare genera un identificador UUID y un archivo de credenciales
~/.cloudflared/<UUID>.json. Anote el UUID; lo necesitará en los pasos siguientes.Escribir el archivo de configuración config.yml
Cree
/etc/cloudflared/config.yml:sudo mkdir -p /etc/cloudflaredContenido del archivo (adapte
<UUID>,votre-domaine.comy el puerto de su aplicación):tunnel: <UUID> credentials-file: /home/<user>/.cloudflared/<UUID>.json ingress: - hostname: app.votre-domaine.com service: http://localhost:3000 - service: http_status:404La última regla —
service: http_status:404sinhostname— es obligatoria: sirve de regla catch-all. Sin ella,cloudflaredse niega a arrancar y devuelve el error"You must specify an ingress rule that matches all incoming requests". Si su aplicación se ejecuta en otro puerto o con otro protocolo, sustituyahttp://localhost:3000en consecuencia (por ejemplohttp://localhost:8080otcp://localhost:22para SSH).Crear el registro DNS y arrancar el túnel
Registre automáticamente el subdominio en su zona de Cloudflare:
cloudflared tunnel route dns mon-tunnel app.votre-domaine.comLuego pruebe el túnel en modo foreground para validar la configuración:
cloudflared tunnel run mon-tunnelAbra
https://app.votre-domaine.comen un navegador. Si la aplicación responde, detenga el proceso (Ctrl+C) y pase al paso siguiente.Instalar cloudflared como servicio systemd
Para que el túnel se reinicie automáticamente al arrancar el servidor, instálelo como demonio del sistema:
sudo cloudflared service install sudo systemctl enable cloudflared sudo systemctl start cloudflared sudo systemctl status cloudflaredEl archivo unit de systemd creado por Cloudflare se encuentra en
/etc/systemd/system/cloudflared.service. Su contenido se parece a esto:[Unit] Description=cloudflared After=network.target [Service] TimeoutStartSec=0 Type=notify ExecStart=/usr/bin/cloudflared --no-autoupdate tunnel run Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.targetCon este servicio activo, el túnel queda operativo desde el arranque del VPS, sin intervención manual.
Integrar cloudflared en un Docker Compose existente
Si su aplicación ya se ejecuta en un stack de Docker Compose, añada un servicio
cloudflareden el mismo archivo. El enfoque por token (sin archivo de credenciales) es el más sencillo para un contenedor:services: app: image: mon-image networks: - internal cloudflared: image: cloudflare/cloudflared:latest command: tunnel --no-autoupdate run environment: - TUNNEL_TOKEN=${TUNNEL_TOKEN} networks: - internal restart: unless-stopped networks: internal:Defina
TUNNEL_TOKENen un archivo.enval mismo nivel. El token se obtiene desde el panel de Cloudflare → Zero Trust → Networks → Tunnels → su túnel → Configure → Conectores Docker. El serviciocloudflaredy su aplicación comparten la redinternal; enconfig.ymlo mediante el token, apunte a la aplicación por su nombre de servicio Docker (http://app:3000en lugar dehttp://localhost:3000).
Configuración posterior a la instalación
Una vez operativo el túnel, algunos ajustes complementarios mejoran la robustez de la configuración.
Recuperar la IP real del cliente. Por defecto, su aplicación recibe las peticiones desde 127.0.0.1 o desde la IP interna del túnel. Para obtener la IP real del visitante, lea la cabecera CF-Connecting-IP que Cloudflare inyecta automáticamente. Configure su aplicación o su reverse proxy local para confiar en esa cabecera.
Cifrado de extremo a extremo. El túnel cifra la conexión entre cloudflared y Cloudflare. La conexión entre cloudflared y su aplicación local va en HTTP por defecto (loopback o red interna de Docker). Si su aplicación expone HTTPS en local, añada originServerName: app.votre-domaine.com en la regla de ingress correspondiente para que cloudflared valide el certificado.
Varios servicios, un solo túnel. Un túnel puede exponer varios servicios en subdominios distintos: basta con añadir entradas adicionales en el bloque ingress de config.yml, antes de la regla catch-all.
Hardening: cierre los puertos 80 y 443 entrantes
La principal ventaja de esta arquitectura es poder cerrar todos los puertos entrantes del VPS. Una vez validado el túnel, aplique estas reglas de UFW:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enableSu aplicación sigue siendo accesible a través del túnel de Cloudflare (que se apoya en conexiones salientes), y el puerto SSH permanece abierto para la administración. Ninguna conexión directa al 80 o al 443 llega ya al servidor. Consulte la guía Configurar UFW en un VPS para la configuración completa.
Cloudflare Tunnel frente a Nginx / Traefik: enfoques complementarios
Una objeción frecuente: «Ya tengo Nginx y Traefik haciendo ese trabajo, ¿por qué añadir una capa de Cloudflare?» La respuesta es que ambos enfoques no resuelven el mismo problema.
Un reverse proxy local (Nginx, Traefik, Caddy) gestiona el enrutamiento entre servicios de la misma red y la renovación del SSL, pero da por supuesto que el tráfico entrante llega al servidor. Si el puerto 443 está bloqueado por la red situada aguas arriba, el reverse proxy no sirve de nada.
Cloudflare Tunnel resuelve precisamente lo que el reverse proxy local no puede: el tráfico llega a Cloudflare sea cual sea la conectividad de red del servidor. Los dos son complementarios: puede perfectamente colocar Traefik detrás del túnel para gestionar el enrutamiento interno y dejar que Cloudflare se encargue del TLS público.
Cloudflare Tunnel frente a un reverse proxy local
Desplace la tabla
| Criterio | Cloudflare Tunnel | Reverse proxy local (Nginx/Traefik) |
|---|---|---|
| Puerto entrante necesario | No — solo conexión saliente | Sí — 80/443 deben estar accesibles |
| TLS público | Gestionado por Cloudflare, automático | Let's Encrypt vía ACME (certbot, Traefik…) |
| IP dinámica / NAT estricto | Transparente — no hay DNS que actualizar | Problemático — requiere un DynDNS o una IP fija |
| Load balancing | Plan de pago (Cloudflare Load Balancing) | Disponible de forma nativa (Traefik, Nginx upstream) |
| Latencia | Ligeramente superior (enrutamiento vía POP de Cloudflare) | Mínima — tráfico directo hacia el servidor |
| Dependencia externa | Sí — Cloudflare debe estar accesible | No — funciona sin terceros |
Resolución de problemas: mensajes de error frecuentes
You must specify an ingress rule that matches all incoming requests
Falta la regla catch-all o está mal ubicada en config.yml. Debe ser la última entrada del bloque ingress, sin hostname, con service: http_status:404.
Unable to locate config file in default locationscloudflared busca su configuración en ~/.cloudflared/config.yml o en /etc/cloudflared/config.yml. Indique la ruta de forma explícita con cloudflared tunnel --config /etc/cloudflared/config.yml run mon-tunnel.
ERR connection to origin timed out en los logs
La aplicación de destino no es accesible desde cloudflared. Compruebe que el servicio local se está ejecutando (curl http://localhost:3000) y que el puerto de config.yml coincide. En un contexto de Docker Compose, utilice el nombre del servicio (http://app:3000) en lugar de localhost.
Token caducado: tunnel credentials file not found o token is expired
Los tokens generados desde la interfaz de Cloudflare tienen una vida útil limitada si el conector nunca se ha registrado. Regenere el token desde Zero Trust → Networks → Tunnels → Configure → Conectores y actualice después la variable TUNNEL_TOKEN de su .env.
Límites del plan Free: load balancing y SSH por túnel
El plan gratuito no admite load balancing entre varios orígenes. El acceso SSH por túnel (cloudflared access ssh) en el plan gratuito requiere una configuración específica de Cloudflare Access y no está activado por defecto. El número de conectores por túnel está limitado a unas pocas instancias en el plan gratuito.
Cloudflare Tunnel como arquitectura de referencia en un VPS
Cloudflare Tunnel ilustra bien lo que hace posible el acceso root en un VPS: instalar cloudflared como demonio del sistema, modificar las reglas del firewall, controlar servicios systemd. En un alojamiento compartido sin acceso root, ninguno de estos pasos es viable: el demonio no se puede instalar, el firewall no está bajo su control y el servicio no se puede configurar para arrancar con el sistema.
Esta arquitectura encaja especialmente en situaciones donde la conectividad de red es limitada o incierta: laboratorios de desarrollo, oficinas con un firewall corporativo estricto, servidores edge o, simplemente, cuando no se quiere exponer una IP pública. Se combina de forma natural con reverse proxies locales como Traefik o Nginx Proxy Manager para el enrutamiento interno, y con el hardening del sistema para cerrar las superficies de ataque directas.