A side project at home needs a public demo URL for a client. A NUC in the office runs an internal tool I want to reach from outside. The old playbook β rent a static IP, forward ports on the router, manage Let’s Encrypt β is all skippable in 2026.
Cloudflare Tunnel (cloudflared) does the dirty work: your machine opens an outbound-only persistent connection to Cloudflare’s edge, and inbound traffic rides that connection back home. No open ports, no public IP, and you get Cloudflare’s DDoS protection and WAF for free.
Why Cloudflare Tunnel
Plenty of alternatives exist, and they target different use cases:
| Tool | Custom domain | Auth | Self-hosted relay | TCP support | Free tier |
|---|---|---|---|---|---|
| Cloudflare Tunnel | β free | β Access built-in | β CF-managed | β | Generous |
| ngrok | Paid | Paid add-on | β | Paid | Connection-capped |
| Tailscale Funnel | Limited | β | P2P-ish | HTTPS only | 3 ports only |
| frp | DIY | DIY | β self-host | β | Your machine |
Tunnel’s real differentiator: it doesn’t just forward traffic β it plugs your service into Cloudflare’s entire Zero Trust platform. Layer Access (SSO, email OTP) on top, pass traffic through WAF, even get browser-rendered SSH without a client. Those are paid upsells on ngrok and simply don’t exist on Funnel.
The 2026 Recommended Flow: Zero Trust Dashboard
Historically cloudflared was configured via local config.yml. In 2026 Cloudflare steers most users to remotely-managed tunnels β config lives in the cloud dashboard, the local cloudflared just needs a token. Bonus: share tunnels across machines, edit ingress without restarts, run multiple replicas for HA.
Go to one.dash.cloudflare.com β Networks β Tunnels β Create a tunnel β Cloudflared. Name it, copy the install command.
Install cloudflared
macOS:
| |
Linux (Debian/Ubuntu):
| |
Docker:
| |
Drop in the token the dashboard gave you:
| |
This registers a systemd service (Linux) or launchd plist (macOS), auto-starts on boot, auto-restarts on crash. Check the dashboard β green Healthy means you’re live.
Pushing localhost:3000 to foo.example.com
Prerequisite: example.com already uses Cloudflare nameservers.
Back on the tunnel page, switch to the Public Hostname tab β Add a public hostname:
| Field | Value |
|---|---|
| Subdomain | foo |
| Domain | example.com |
| Service Type | HTTP |
| URL | localhost:3000 |
Save. Cloudflare auto-creates the DNS CNAME foo.example.com β <tunnel-uuid>.cfargotunnel.com. Hit https://foo.example.com β the cert comes from Cloudflare’s edge; your local box needs zero TLS config.
Gate It With Zero Trust Access
Don’t want random scanners finding your demo URL? Put a login wall in front:
Zero Trust β Access β Applications β Add an application β Self-hosted
- Application domain:
foo.example.com - Add policy: Action = Allow, Include = Emails ending in
@yourco.com(or a specific email list) - Identity provider: the default One-time PIN (email OTP) works out of the box, or hook up Google SSO / GitHub via Settings β Authentication
Next visit to foo.example.com lands on a Cloudflare login screen first. Free up to 50 users.
TryCloudflare: Disposable Demo Tunnels
Need to show a webhook or demo in 30 seconds, not even wanting to open an account:
| |
Prints https://<random-words>.trycloudflare.com β random subdomain, traffic routed back to your localhost. Dies with the process. Great for temporary use, not production, rate-limited.
Local config.yml (for the GitOps crowd)
To keep tunnel config in Git or drive it from Terraform, the old workflow still works:
| |
~/.cloudflared/config.yml:
| |
Run: cloudflared tunnel run dev-laptop
Footguns I’ve Stepped On
Always include the catch-all ingress. Without - service: http_status:404, cloudflared refuses to start:
| |
WebSockets just work. Default-on since 2022, no flag needed. Next.js HMR, Socket.IO, fine.
SSH / RDP also work. Ingress entry service: ssh://localhost:22, then enable Browser rendering in the Access app β users get an in-browser terminal with no client install.
Run the same tunnel on multiple hosts. Re-use the same token on a second machine and Cloudflare handles HA / load balancing automatically. Reboot one host, service stays up.
Per-hostname originRequest. Each ingress can independently set httpHostHeader, connectTimeout, noTLSVerify β no need to change global settings for one service.
Pricing
- Tunnel itself: 100% free, unmetered bandwidth, unlimited tunnels
- Zero Trust Access: free up to 50 users, then Cloudflare One pay-as-you-go ~$7/user/month
- No egress fees, no connection limits
For individuals and small teams, it’s effectively free.
Notable 2025β2026 Updates
Dashboard consolidation: dash.teams.cloudflare.com is fully retired; everything lives at one.dash.cloudflare.com. If you land on an old tutorial, update the URL.
WARP Connector GA: where Tunnel exposes individual services, WARP Connector brings entire subnets onto Cloudflare’s network. Site-to-site VPN replacement; complements Tunnel for full-network reach.
Cloudflare One rebrand: Access, Gateway, Tunnel, WARP, CASB, DLP, Email Security merged into a single SSE platform. One Zero Trust menu now covers all enterprise network security.
Terraform provider v5 is stable: full IaC for tunnel resources, trivial multi-environment deployments.
QUIC as default: cloudflared now uses QUIC (HTTP/3) by default, faster connection establishment and more resilient on flaky networks.
When Not to Use Tunnel
Great tool, not universal:
- Want strict peer-to-peer with no third party: use Tailscale / WireGuard
- Regulatory rules require non-US / non-CF transit: self-host frp or enterprise VPN
- Huge static file traffic: Cloudflare Pages / R2 is a better fit than tunneling
- Five-minute demo with no domain: TryCloudflare or ngrok is faster
Closing
Demo environments, webhook receivers, showing your coworker a work-in-progress β in 2026 Cloudflare Tunnel is almost always the lowest-friction answer. Generous free tier, security handled by Cloudflare, and the same setup carries from dev to production.
Three steps to live: brew install cloudflared β dashboard creates tunnel β paste token. Everything complicated lives in a cloud UI.
