{
  "$type": "site.standard.document",
  "description": "I needed a way to expose local dev servers to the internet for webhook testing. ngrok works, but Cloudflare Tunnel is free and lets you use your own",
  "path": "/configuring-cloudflare-tunnel-to-expose-servers-for-local-development-webhooks-etc/",
  "publishedAt": "2025-11-29T01:43:00.000Z",
  "site": "at://did:plc:bryys25pc2fnagnyxqgsglhd/site.standard.publication/3mn26bjkkmh23",
  "tags": [
    "Tools",
    "Web"
  ],
  "textContent": "I needed a way to expose local dev servers to the internet for webhook testing. ngrok works, but Cloudflare Tunnel is free and lets you use your own domain whereas ngrok is free, but you pay $10 (as of 20251201) per custom domain.\n\nPrerequisite: Cloudflare must already be managing DNS for your domain.\n\nINSTALL AND LOGIN\n\nbrew install cloudflared\ncloudflared tunnel login\n\nThe login command opens your browser to authenticate with Cloudflare.\n\nCREATE A TUNNEL\n\nYou only need one tunnel. You can route multiple subdomains through the same tunnel. I'll use dev as the tunnel name in these examples:\n\ncloudflared tunnel create dev\n\nThis creates the tunnel and stores credentials at ~/.cloudflared/<tunnel-id>.json.\n\nCREATE DNS ROUTES\n\nThis creates CNAME records pointing your subdomains to the tunnel:\n\ncloudflared tunnel route dns dev dev.yourdomain.com\ncloudflared tunnel route dns dev dev-backend.yourdomain.com\n\nTip: Keep it simple—use subdomains under the same root domain even for different projects. Instead of dev.project1.com and dev.project2.com, use dev.yourdomain.com and dev2.yourdomain.com. Otherwise, you'd find the cloudflared tunnel route dns <name> <subdomain> command creating CNAME records in your first domain. It's apparently not supported unless you use the web dashboard to manage your tunnels. It's much simpler just to stick to 1 root domain.\n\nTo remove a route, delete the CNAME record from the Cloudflare dashboard.\n\nCONFIGURE AND RUN\n\nCreate ~/.cloudflared/config.yml:\n\ntunnel: <tunnel-id>\ncredentials-file: /Users/you/.cloudflared/<tunnel-id>.json\n\ningress:\n  - hostname: dev.yourdomain.com\n    service: http://localhost:5174\n  - hostname: dev-backend.yourdomain.com\n    service: http://localhost:4002\n  - service: http_status:404\n\nThe last rule is a required catch-all for unmatched requests.\n\nThen normally, you'd just run:\n\ncloudflared tunnel run dev\n\nBoth subdomains now route to their respective local ports.",
  "title": "Configuring Cloudflare Tunnel to Expose Servers for Local Development, Webhooks etc"
}