Saltar al contenido principal

Cloudflare Tunnel (cloudflared)

El túnel más rápido y con menos limitaciones. No requiere cuenta para uso básico. Usa la red global de Cloudflare.

Límites del plan gratuito

ParámetroValor
CostoCompletamente gratis
Registro❌ No requerido (modo rápido)
HTTPS✅ Automático
Velocidad⚡ Muy alta (red de Cloudflare)
URLAleatoria en cada sesión
URL fija✅ Con cuenta gratuita de Cloudflare
Límite de requestsSin límite práctico
Múltiples túneles✅ Con cuenta

Instalación

# Windows (PowerShell como administrador):
winget install --id Cloudflare.cloudflared

# Mac:
brew install cloudflare/cloudflare/cloudflared

# Linux (Debian/Ubuntu):
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb

# Verificar instalación:
cloudflared --version

Uso rápido (sin cuenta)

La forma más rápida — cero configuración, cero registro:

# Tu app corre en puerto 3000:
cloudflared tunnel --url http://localhost:3000

Salida:

2024-01-15T10:30:00Z INF Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that:
...
+-------------------------------------------------------+
| Your quick Tunnel has been created! Visit it at: |
| https://random-words-here.trycloudflare.com |
+-------------------------------------------------------+

¡Listo! Comparte esa URL y cualquiera puede acceder a tu localhost.

# Otros ejemplos:
cloudflared tunnel --url http://localhost:8080 # puerto diferente
cloudflared tunnel --url http://localhost:5173 # Vite dev server
cloudflared tunnel --url http://localhost:8000 # FastAPI/Django

Uso con cuenta (URL fija y persistente)

Con una cuenta gratuita de Cloudflare puedes tener túneles con nombres fijos:

1. Autenticarse

cloudflared tunnel login
# Abre el browser para autenticarse con tu cuenta de Cloudflare

2. Crear el túnel

# Crear túnel con nombre
cloudflared tunnel create mi-proyecto-dev

# Ver lista de túneles
cloudflared tunnel list

# Salida:
# ID NAME CREATED
# f6c9a8b2-xxxx-xxxx-xxxx-xxxxxxxxxxxx mi-proyecto-dev 2024-01-15T...

3. Configurar el túnel

# ~/.cloudflared/config.yml
tunnel: f6c9a8b2-xxxx-xxxx-xxxx-xxxxxxxxxxxx # ID del túnel
credentials-file: /home/usuario/.cloudflared/f6c9a8b2-xxxx.json

ingress:
- hostname: dev.mi-dominio.com # Tu dominio (si tienes uno en Cloudflare)
service: http://localhost:3000
- service: http_status:404 # Default: 404 para todo lo demás

4. Crear registro DNS

# Apuntar subdominio a tu túnel (si tienes dominio en Cloudflare)
cloudflared tunnel route dns mi-proyecto-dev dev.mi-dominio.com

5. Correr el túnel

# Con archivo de configuración:
cloudflared tunnel run mi-proyecto-dev

# O especificando directamente:
cloudflared tunnel --url http://localhost:3000 run mi-proyecto-dev

Caso de uso: Webhooks de Stripe en desarrollo

# 1. Levantar tu API local
npm run dev # → corre en localhost:3000

# 2. En otra terminal, abrir el túnel
cloudflared tunnel --url http://localhost:3000
# → https://abc-def-ghi.trycloudflare.com

# 3. Configurar el webhook en Stripe Dashboard:
# https://dashboard.stripe.com/test/webhooks
# → Add endpoint: https://abc-def-ghi.trycloudflare.com/webhook/stripe
# → Events: payment_intent.succeeded, payment_intent.payment_failed

# 4. En tu código, recibir el webhook:
// src/routes/webhook.ts
import Stripe from 'stripe'
import express from 'express'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const router = express.Router()

// IMPORTANTE: usar express.raw() antes de este router para preservar el body
router.post('/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['stripe-signature']!

let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET! // del Dashboard de Stripe
)
} catch (err) {
console.error('Webhook signature inválida:', err)
return res.status(400).send(`Webhook Error: ${err}`)
}

switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object as Stripe.PaymentIntent
console.log('✅ Pago exitoso:', paymentIntent.amount)
// → actualizar orden en DB, enviar email de confirmación, etc.
break

case 'payment_intent.payment_failed':
console.log('❌ Pago fallido')
// → notificar al usuario
break
}

res.json({ received: true })
})

export default router

Caso de uso: OAuth de GitHub App en desarrollo

# 1. En GitHub Developer Settings → New GitHub App:
# Callback URL: https://abc-def-ghi.trycloudflare.com/auth/callback

# 2. Levantar app + túnel como arriba

# 3. En tu código:
// src/routes/auth.ts
import { Octokit } from '@octokit/oauth-app'

const CLIENT_ID = process.env.GITHUB_CLIENT_ID!
const CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET!

// Redirigir a GitHub para autorización
app.get('/auth/github', (req, res) => {
const authUrl = `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&scope=repo,user`
res.redirect(authUrl)
})

// GitHub redirige aquí después de que el usuario autoriza
app.get('/auth/callback', async (req, res) => {
const { code } = req.query

// Intercambiar code por access token
const response = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code })
})

const { access_token } = await response.json()

// Obtener datos del usuario
const octokit = new Octokit({ auth: access_token })
const { data: user } = await octokit.request('GET /user')

console.log('Usuario autenticado:', user.login)
res.json({ user: user.login, token: access_token })
})

Caso de uso: Probar en móvil

# 1. Levantar tu app (Vite, Next.js dev, etc.)
npm run dev # → localhost:5173

# 2. Abrir túnel
cloudflared tunnel --url http://localhost:5173

# 3. En tu celular:
# Abrir el browser → navegar a https://xxx.trycloudflare.com
# ¡Tu app en el celular!

Extra: hacer que Vite acepte conexiones externas (para probar desde la misma red sin túnel):

// vite.config.ts
export default defineConfig({
server: {
host: '0.0.0.0', // ← Escuchar en todas las interfaces
port: 5173,
}
})
// Luego acceder desde celular: http://192.168.1.X:5173

Correr como servicio (siempre activo)

# Instalar como servicio del sistema (requiere cuenta y config.yml)
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

# Ver estado
sudo systemctl status cloudflared

⚠️ Limitaciones importantes

  • No compartir URLs de túnel con datos sensibles ni usarlas en producción
  • Los túneles rápidos (sin cuenta) duran ~24 horas y la URL cambia en cada reinicio
  • Variables de entorno: nunca pongas secrets en la URL del túnel
  • Para demos a clientes: usar el túnel solo durante la demo, luego cerrar
  • En CI/CD: no usar túneles, usar URLs reales de staging