<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Zero-Trust on recca0120 Tech Notes</title><link>https://recca0120.github.io/en/tags/zero-trust/</link><description>Recent content in Zero-Trust on recca0120 Tech Notes</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 14 Apr 2026 20:00:00 +0800</lastBuildDate><atom:link href="https://recca0120.github.io/en/tags/zero-trust/index.xml" rel="self" type="application/rss+xml"/><item><title>Cloudflare Tunnel in 2026: Expose localhost Without Opening Ports or Buying an IP</title><link>https://recca0120.github.io/en/2026/04/14/cloudflare-tunnel-2026/</link><pubDate>Tue, 14 Apr 2026 20:00:00 +0800</pubDate><guid>https://recca0120.github.io/en/2026/04/14/cloudflare-tunnel-2026/</guid><description>&lt;img src="https://recca0120.github.io/" alt="Featured image of post Cloudflare Tunnel in 2026: Expose localhost Without Opening Ports or Buying an IP" /&gt;&lt;p&gt;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&amp;rsquo;s Encrypt — is all skippable in 2026.&lt;/p&gt;
&lt;p&gt;Cloudflare Tunnel (cloudflared) does the dirty work: your machine opens an &lt;strong&gt;outbound-only&lt;/strong&gt; persistent connection to Cloudflare&amp;rsquo;s edge, and inbound traffic rides that connection back home. No open ports, no public IP, and you get Cloudflare&amp;rsquo;s DDoS protection and WAF for free.&lt;/p&gt;
&lt;h2 id="why-cloudflare-tunnel"&gt;&lt;a href="#why-cloudflare-tunnel" class="header-anchor"&gt;&lt;/a&gt;Why Cloudflare Tunnel
&lt;/h2&gt;&lt;p&gt;Plenty of alternatives exist, and they target different use cases:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Tool&lt;/th&gt;
 &lt;th&gt;Custom domain&lt;/th&gt;
 &lt;th&gt;Auth&lt;/th&gt;
 &lt;th&gt;Self-hosted relay&lt;/th&gt;
 &lt;th&gt;TCP support&lt;/th&gt;
 &lt;th&gt;Free tier&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Cloudflare Tunnel&lt;/td&gt;
 &lt;td&gt;✅ free&lt;/td&gt;
 &lt;td&gt;✅ Access built-in&lt;/td&gt;
 &lt;td&gt;❌ CF-managed&lt;/td&gt;
 &lt;td&gt;✅&lt;/td&gt;
 &lt;td&gt;Generous&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ngrok&lt;/td&gt;
 &lt;td&gt;Paid&lt;/td&gt;
 &lt;td&gt;Paid add-on&lt;/td&gt;
 &lt;td&gt;❌&lt;/td&gt;
 &lt;td&gt;Paid&lt;/td&gt;
 &lt;td&gt;Connection-capped&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Tailscale Funnel&lt;/td&gt;
 &lt;td&gt;Limited&lt;/td&gt;
 &lt;td&gt;❌&lt;/td&gt;
 &lt;td&gt;P2P-ish&lt;/td&gt;
 &lt;td&gt;HTTPS only&lt;/td&gt;
 &lt;td&gt;3 ports only&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;frp&lt;/td&gt;
 &lt;td&gt;DIY&lt;/td&gt;
 &lt;td&gt;DIY&lt;/td&gt;
 &lt;td&gt;✅ self-host&lt;/td&gt;
 &lt;td&gt;✅&lt;/td&gt;
 &lt;td&gt;Your machine&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Tunnel&amp;rsquo;s real differentiator&lt;/strong&gt;: it doesn&amp;rsquo;t just forward traffic — it plugs your service into Cloudflare&amp;rsquo;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&amp;rsquo;t exist on Funnel.&lt;/p&gt;
&lt;h2 id="the-2026-recommended-flow-zero-trust-dashboard"&gt;&lt;a href="#the-2026-recommended-flow-zero-trust-dashboard" class="header-anchor"&gt;&lt;/a&gt;The 2026 Recommended Flow: Zero Trust Dashboard
&lt;/h2&gt;&lt;p&gt;Historically cloudflared was configured via local &lt;code&gt;config.yml&lt;/code&gt;. In 2026 Cloudflare steers most users to &lt;strong&gt;remotely-managed tunnels&lt;/strong&gt; — 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.&lt;/p&gt;
&lt;p&gt;Go to &lt;a class="link" href="https://one.dash.cloudflare.com" target="_blank" rel="noopener"
 &gt;one.dash.cloudflare.com&lt;/a&gt; → &lt;strong&gt;Networks → Tunnels → Create a tunnel → Cloudflared&lt;/strong&gt;. Name it, copy the install command.&lt;/p&gt;
&lt;h2 id="install-cloudflared"&gt;&lt;a href="#install-cloudflared" class="header-anchor"&gt;&lt;/a&gt;Install cloudflared
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install cloudflared
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux (Debian/Ubuntu)&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -L https://pkg.cloudflare.com/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sudo bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install cloudflared
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name cf-tunnel --restart unless-stopped &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cloudflare/cloudflared:latest &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tunnel --no-autoupdate run --token eyJhbGci...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Drop in the token the dashboard gave you:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cloudflared service install eyJhbGci...TOKEN...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This registers a systemd service (Linux) or launchd plist (macOS), auto-starts on boot, auto-restarts on crash. Check the dashboard — green &lt;strong&gt;Healthy&lt;/strong&gt; means you&amp;rsquo;re live.&lt;/p&gt;
&lt;h2 id="pushing-localhost3000-to-fooexamplecom"&gt;&lt;a href="#pushing-localhost3000-to-fooexamplecom" class="header-anchor"&gt;&lt;/a&gt;Pushing localhost:3000 to foo.example.com
&lt;/h2&gt;&lt;p&gt;Prerequisite: &lt;code&gt;example.com&lt;/code&gt; already uses Cloudflare nameservers.&lt;/p&gt;
&lt;p&gt;Back on the tunnel page, switch to the &lt;strong&gt;Public Hostname&lt;/strong&gt; tab → &lt;strong&gt;Add a public hostname&lt;/strong&gt;:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Subdomain&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;foo&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Domain&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;example.com&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Service Type&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;HTTP&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;URL&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;localhost:3000&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Save. Cloudflare auto-creates the DNS CNAME &lt;code&gt;foo.example.com → &amp;lt;tunnel-uuid&amp;gt;.cfargotunnel.com&lt;/code&gt;. Hit &lt;code&gt;https://foo.example.com&lt;/code&gt; — the cert comes from Cloudflare&amp;rsquo;s edge; your local box needs zero TLS config.&lt;/p&gt;
&lt;h2 id="gate-it-with-zero-trust-access"&gt;&lt;a href="#gate-it-with-zero-trust-access" class="header-anchor"&gt;&lt;/a&gt;Gate It With Zero Trust Access
&lt;/h2&gt;&lt;p&gt;Don&amp;rsquo;t want random scanners finding your demo URL? Put a login wall in front:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero Trust → Access → Applications → Add an application → Self-hosted&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Application domain: &lt;code&gt;foo.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add policy: Action = &lt;strong&gt;Allow&lt;/strong&gt;, Include = &lt;strong&gt;Emails ending in &lt;code&gt;@yourco.com&lt;/code&gt;&lt;/strong&gt; (or a specific email list)&lt;/li&gt;
&lt;li&gt;Identity provider: the default &lt;strong&gt;One-time PIN&lt;/strong&gt; (email OTP) works out of the box, or hook up Google SSO / GitHub via &lt;strong&gt;Settings → Authentication&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next visit to &lt;code&gt;foo.example.com&lt;/code&gt; lands on a Cloudflare login screen first. Free up to &lt;strong&gt;50 users&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="trycloudflare-disposable-demo-tunnels"&gt;&lt;a href="#trycloudflare-disposable-demo-tunnels" class="header-anchor"&gt;&lt;/a&gt;TryCloudflare: Disposable Demo Tunnels
&lt;/h2&gt;&lt;p&gt;Need to show a webhook or demo in 30 seconds, not even wanting to open an account:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cloudflared tunnel --url http://localhost:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Prints &lt;code&gt;https://&amp;lt;random-words&amp;gt;.trycloudflare.com&lt;/code&gt; — random subdomain, traffic routed back to your localhost. Dies with the process. Great for temporary use, not production, rate-limited.&lt;/p&gt;
&lt;h2 id="local-configyml-for-the-gitops-crowd"&gt;&lt;a href="#local-configyml-for-the-gitops-crowd" class="header-anchor"&gt;&lt;/a&gt;Local config.yml (for the GitOps crowd)
&lt;/h2&gt;&lt;p&gt;To keep tunnel config in Git or drive it from Terraform, the old workflow still works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cloudflared tunnel login
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cloudflared tunnel create dev-laptop
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cloudflared tunnel route dns dev-laptop foo.example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;~/.cloudflared/config.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tunnel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;dev-laptop&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;credentials-file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/Users/me/.cloudflared/&amp;lt;UUID&amp;gt;.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;ingress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;foo.example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;api.example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://localhost:4000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;originRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;connectTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;30s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;noTLSVerify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http_status:404&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Run: &lt;code&gt;cloudflared tunnel run dev-laptop&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="footguns-ive-stepped-on"&gt;&lt;a href="#footguns-ive-stepped-on" class="header-anchor"&gt;&lt;/a&gt;Footguns I&amp;rsquo;ve Stepped On
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Always include the catch-all ingress.&lt;/strong&gt; Without &lt;code&gt;- service: http_status:404&lt;/code&gt;, cloudflared refuses to start:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;ingress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;foo.example.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http_status:404 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# mandatory final entry&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;WebSockets just work.&lt;/strong&gt; Default-on since 2022, no flag needed. Next.js HMR, Socket.IO, fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSH / RDP also work.&lt;/strong&gt; Ingress entry &lt;code&gt;service: ssh://localhost:22&lt;/code&gt;, then enable &lt;strong&gt;Browser rendering&lt;/strong&gt; in the Access app — users get an in-browser terminal with no client install.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Run the same tunnel on multiple hosts.&lt;/strong&gt; Re-use the same token on a second machine and Cloudflare handles HA / load balancing automatically. Reboot one host, service stays up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Per-hostname originRequest.&lt;/strong&gt; Each ingress can independently set &lt;code&gt;httpHostHeader&lt;/code&gt;, &lt;code&gt;connectTimeout&lt;/code&gt;, &lt;code&gt;noTLSVerify&lt;/code&gt; — no need to change global settings for one service.&lt;/p&gt;
&lt;h2 id="pricing"&gt;&lt;a href="#pricing" class="header-anchor"&gt;&lt;/a&gt;Pricing
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tunnel itself: 100% free&lt;/strong&gt;, unmetered bandwidth, unlimited tunnels&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero Trust Access: free up to 50 users&lt;/strong&gt;, then Cloudflare One pay-as-you-go ~$7/user/month&lt;/li&gt;
&lt;li&gt;No egress fees, no connection limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For individuals and small teams, it&amp;rsquo;s effectively free.&lt;/p&gt;
&lt;h2 id="notable-20252026-updates"&gt;&lt;a href="#notable-20252026-updates" class="header-anchor"&gt;&lt;/a&gt;Notable 2025–2026 Updates
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Dashboard consolidation&lt;/strong&gt;: &lt;code&gt;dash.teams.cloudflare.com&lt;/code&gt; is fully retired; everything lives at &lt;code&gt;one.dash.cloudflare.com&lt;/code&gt;. If you land on an old tutorial, update the URL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WARP Connector GA&lt;/strong&gt;: where Tunnel exposes individual services, WARP Connector brings &lt;strong&gt;entire subnets&lt;/strong&gt; onto Cloudflare&amp;rsquo;s network. Site-to-site VPN replacement; complements Tunnel for full-network reach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare One rebrand&lt;/strong&gt;: Access, Gateway, Tunnel, WARP, CASB, DLP, Email Security merged into a single SSE platform. One Zero Trust menu now covers all enterprise network security.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Terraform provider v5&lt;/strong&gt; is stable: full IaC for tunnel resources, trivial multi-environment deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;QUIC as default&lt;/strong&gt;: cloudflared now uses QUIC (HTTP/3) by default, faster connection establishment and more resilient on flaky networks.&lt;/p&gt;
&lt;h2 id="when-not-to-use-tunnel"&gt;&lt;a href="#when-not-to-use-tunnel" class="header-anchor"&gt;&lt;/a&gt;When Not to Use Tunnel
&lt;/h2&gt;&lt;p&gt;Great tool, not universal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Want strict peer-to-peer with no third party&lt;/strong&gt;: use Tailscale / WireGuard&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regulatory rules require non-US / non-CF transit&lt;/strong&gt;: self-host frp or enterprise VPN&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Huge static file traffic&lt;/strong&gt;: Cloudflare Pages / R2 is a better fit than tunneling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Five-minute demo with no domain&lt;/strong&gt;: TryCloudflare or ngrok is faster&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="closing"&gt;&lt;a href="#closing" class="header-anchor"&gt;&lt;/a&gt;Closing
&lt;/h2&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Three steps to live: &lt;code&gt;brew install cloudflared&lt;/code&gt; → dashboard creates tunnel → paste token. Everything complicated lives in a cloud UI.&lt;/p&gt;
&lt;h2 id="references"&gt;&lt;a href="#references" class="header-anchor"&gt;&lt;/a&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/" target="_blank" rel="noopener"
 &gt;Cloudflare Tunnel Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cloudflare/cloudflared" target="_blank" rel="noopener"
 &gt;cloudflared on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://one.dash.cloudflare.com" target="_blank" rel="noopener"
 &gt;Zero Trust Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/" target="_blank" rel="noopener"
 &gt;TryCloudflare&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cloudflare.com/plans/zero-trust-services/" target="_blank" rel="noopener"
 &gt;Cloudflare Access Pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>