Share localhost with three named people: Cloudflare's Protected Quick Tunnels vs Tailscale Funnel
cloudflared 2026.9.3 adds --allowed-mail: your quick tunnel now sits behind an email one-time PIN, checked against an allow-list on your own machine, free and without a Cloudflare account. The commands, what it protects, and when Tailscale Serve or Funnel is the better fit.
if.if.codesOct 5, 2026 · 4 min read#cloudflare#tailscale#tunnels#securityAI-assisted
You have a dev server on localhost:8080 and three people who need to click around it: a client, a designer, a colleague on another network. For years the quickest answer has been cloudflared tunnel --url, which hands you a random trycloudflare.com URL. It works, and it also puts an unauthenticated app on the public internet for anyone who gets hold of the link.
Adding a layer of protection to a local development server.
On 2 October Cloudflare added a fix for that: Protected Quick Tunnels. From cloudflared 2026.9.3, one extra flag puts the tunnel behind an email one-time PIN, and only addresses on your list get through. No Cloudflare account on either side, no dashboard, still free. This guide takes about ten minutes.
1. Update cloudflared
You need 2026.9.3 or later. Check what you have:
cloudflared --version
If it is older, update it with whatever installed it:
# macOS (Homebrew)
brew upgrade cloudflared
# Debian/Ubuntu installed from Cloudflare's apt reposudo apt-get update && sudo apt-get install --only-upgrade cloudflared
# installed as a plain binary
cloudflared update
2. Start something to share
Any local HTTP server will do. If you just want to try the flow:
mkdir -p /tmp/demo && cd /tmp/demo
echo'<h1>hello from my laptop</h1>' > index.html
python3 -m http.server 8080
3. Open a protected tunnel
In a second terminal, start the tunnel with one --allowed-mail per person:
cloudflared prints the trycloudflare.com URL as before. Send it to your three people.
4. What your visitor sees
Instead of your app, the visitor gets a sign-in page asking for an email address. Cloudflare Access sends a one-time PIN to that mailbox; entering it proves they own the address. Then the decision happens on your machine: cloudflared compares the verified address with the list you passed on the command line and lets the request through or not. Cloudflare's post puts it plainly: your guest list never leaves your machine.
The email one-time PIN acts as a gatekeeper for the tunnel.
Need the backend underneath done properly?I build and harden Go and Python backends. The estimate is free.
What it protects, and what it does not
From the announcement and the Quick Tunnels docs, the important details are:
Sessions last up to four hours, less if the visitor's Access sign-in expires sooner, and end the moment you stop cloudflared. Stopping the process is your kill switch.
The allow-list lives in memory on your machine. Change it by restarting the tunnel with different flags. That also means a new random URL.
Access credentials are stripped before the request is forwarded to your app, so your app is not handed the visitor's sign-in token.
It proves an email address, nothing more. If Bob forwards the PIN email or signs in on a shared machine, the tunnel cannot tell. Your app's own login, if it has one, still matters.
Quick Tunnel limits still apply: up to 200 in-flight requests per tunnel (more get a 429), no Server-Sent Events, and no uptime guarantee. If your app streams with SSE, for example a chat UI or live logs, it will not work through a quick tunnel, protected or not.
For a demo, a review or a quick look at a webhook payload page, that is a big improvement over a public, unauthenticated URL, at the cost of one flag.
When Tailscale is the better fit
Tailscale has two related commands, and they answer different questions.
Tailscale Serve: share inside your tailnet
tailscale serve 8080
This publishes the local port over HTTPS to devices in your tailnet only. If the people you are sharing with are already on your tailnet, such as teammates or your own phone, this is the better option: no PINs, no four-hour limit, and the app is never on the public internet. People outside your tailnet need to join it or have the device shared with them, which means a Tailscale account and client on their side.
Tailscale Funnel: public, without the allow-list
tailscale funnel 8080
Funnel routes traffic from the public internet to your local service, and visitors do not need Tailscale accounts. That makes it the closest match to a quick tunnel, but Funnel itself adds no sign-in step: anyone with the URL gets in, and your app has to do its own authentication. Funnel also has setup requirements: MagicDNS and HTTPS certificates enabled for the tailnet, a funnel node attribute in your tailnet policy file, and only ports 443, 8443 and 10000 can be exposed. In exchange you get a stable ts.net hostname instead of a new random URL every time.
Which one to use
A few named outsiders, for an afternoon: Protected Quick Tunnel. One command, no accounts, email-gated, gone when you hit Ctrl-C.
Teammates who already run Tailscale:tailscale serve. Private by default, no time limit.
A public endpoint with a stable URL, for example a webhook receiver that a third-party service must reach: Tailscale Funnel, or a named Cloudflare Tunnel with an Access policy. Both need an account. Neither a protected quick tunnel nor an email PIN works for a machine caller anyway.
Anything that streams with SSE or needs to stay up: not a quick tunnel.
The habit worth changing today is small: if you already type cloudflared tunnel --url, add --allowed-mail every time. A dev server reachable only by the three people you meant to share it with is a much easier thing to defend.
Choosing between private network sharing and public internet access.