Tailscale Funnel fits when you already run Tailscale and a ts.net address on port 443, 8443 or 10000 is good enough for whoever opens the link. If you need your own domain, a link that works the moment you send it, or downloads faster than a few megabits a second, the alternative is a tunnel service such as LocalCan, ngrok or Cloudflare Tunnel. In my test from one Mac, a new Funnel URL took anywhere from under 2 minutes to 10 minutes to serve its first request, and downloads through Funnel ran at a median of 7 Mbps. A new LocalCan Public URL (also called a tunnel) answered within 1.5 seconds, and downloads ran at a median of 57 Mbps.
I build LocalCan, so weigh the comparison with that in mind. Prices and limits checked October 2026, and each section links its source. An AI assistant helped draft this post. The commands and numbers come from my own runs.
What are Tailscale Funnel's limits?
Funnel publishes local services from a device in your tailnet to the public internet. Tailscale's Funnel documentation↗ still calls the feature a beta and lists these limits:
- Names come only from your tailnet's domain,
<device>.<tailnet>.ts.net. - Funnel listens on ports 443, 8443 and 10000 and on nothing else. Your app can be on any local port, only the public port is limited.
- Traffic is "subject to non-configurable bandwidth limits", and Tailscale publishes no number for them.
- Public DNS records "can take up to 10 minutes to show up".
- It needs MagicDNS, HTTPS certificates and a
funnelattribute in the tailnet policy file. On macOS, sharing a port needs the App Store or the Standalone app. - Requesting certificates too often can hit Let's Encrypt's rate limits, and Tailscale warns that you may then wait 34 hours.
The tailscale funnel reference↗ adds one more: "only http://127.0.0.1↗ is currently supported for proxies", so Funnel forwards to services on the device itself.
Funnel is on every plan, including the free Personal plan, which Tailscale says is "only suitable for non-commercial use". Standard costs $8 per user per month and Premium $18 (Tailscale pricing↗).
How long does a new Funnel URL take to work?
The command prints the URL at once:
$ tailscale funnel --bg 3000
Available on the internet:
https://bench-2.<tailnet>.ts.net/
|-- proxy http://127.0.0.1:3000
Funnel started and running in the background.
To disable the proxy, run: tailscale funnel --https=443 off
To time it, I gave the Mac a device name it had never used and started Funnel. In a loop, every couple of seconds, a script asked the four public nameservers for ts.net about the new name and, as soon as one of them knew it, sent a request through Funnel's relay. On the first timed run, the name reached one nameserver fast, and the relay then closed every TLS handshake for four and a half minutes, which fits a device still waiting for its certificate:
Seconds after tailscale funnel | What happened |
|---|---|
| 1.6 | The first of four ts.net nameservers returns the relay's address |
| 1.6 to 273.8 | 118 requests, each one closed by the relay during the TLS handshake |
| 275.2 | The first request that got a 200 is sent |
| 299.8 | Two more nameservers return the address |
| 302.4 | The last nameserver returns it |
On the second timed run, DNS was the wait. None of the four nameservers knew the name for 600.9 seconds, the full 10 minutes Tailscale's documentation↗ warns about, and the first request after that was served. An earlier run that I checked only once was serving at 109 seconds.
A visitor who opens the link early can stay locked out for up to 5 minutes after the name is live. In the first timed run, three of the four nameservers had no record for the name until about 300 seconds, and a resolver that hears "no such name" keeps that answer for the 5 minutes ts.net sets.
Restarting Funnel on a name that already has its certificate and DNS record is quicker. In three restarts, a request sent right away hung until my 8-second timeout, the retry at about 9.5 seconds was served, and the first response arrived 10 to 17 seconds after the command.
The same test against a LocalCan quick tunnel got its first response 0.7 to 1.5 seconds after the command, in 6 runs out of 6. Every *.localcan.dev name is covered by one wildcard certificate, so a new name has no certificate to wait for. With --json, the command prints a line with the Public URL as soon as it is live:
localcan http 3000 --json
{"event":"forwarding","time":"2026-10-13T21:09:56+02:00","scheme":"https","url":"golden-lion.localcan.dev","target":"localhost:3000"}
How much bandwidth does Tailscale Funnel get?
A median of 7 Mbps on a 66.8 Mbps uplink, about 8 times slower than LocalCan in my test. I served a 100 MB file from a Node server on port 3000, the same download as in my earlier tunnel speed test, and fetched it 10 times through each service from the test Mac, alternating the two. Funnel's relay was in Frankfurt, LocalCan's edge in Amsterdam. The uplink figure comes from macOS networkQuality, and neither service could go faster than it. I also made 20 small requests through each, opening a new connection every time.
| Tailscale Funnel | LocalCan | |
|---|---|---|
| 100 MB download, median of 10 | 7.0 Mbps | 56.6 Mbps |
| Slowest and fastest download | 3.1 to 8.3 Mbps | 27.4 to 61.2 Mbps |
| Time for 100 MB, median of 10 | 119 s | 15 s |
| Time to first byte, median of 20 | 663 ms | 233 ms |
| TLS handshake done, median of 20 | 509 ms | 120 ms |
Funnel started at 3.1 Mbps and settled between 6.3 and 8.3 Mbps from the fourth download on. LocalCan's median sat close to what the line could upload.
Funnel's handshakes are slower because its relay passes the encrypted connection through to your device and "cannot decrypt data sent over this proxy" (how Funnel works↗), so every TLS handshake makes the round trip to your device. LocalCan's edge completes the handshake in the data centre.
During the downloads, tailscale status --json showed the active Funnel relay with no direct address to the test Mac, so the traffic went through Tailscale's DERP server in Frankfurt. Tailscale does not publish Funnel's bandwidth limit, so I can't say whether the limit or the DERP server set the speed.
Can Tailscale Funnel use a custom domain?
No. Funnel only uses names in your tailnet's ts.net domain, and its certificates cover only those names (Funnel documentation↗). If you want to stay on Tailscale, one workaround is a reverse proxy for your domain on a server you run, joined to the tailnet and forwarding to your device's tailnet address. To put localhost on your own domain without a server, use a service that issues the certificate for that domain:
- LocalCan: add a subdomain such as
preview.example.comon the Dashboard with one CNAME record, then runlocalcan http 3000 --domain preview.example.com(custom domains). Solo includes 2 custom domains for $10 a month, or $96 a year (LocalCan pricing). - Cloudflare Tunnel: a named tunnel is free (Cloudflare Tunnel↗ is on every plan), and the domain has to be on Cloudflare (Cloudflare Tunnel setup↗).
- ngrok: your own domain needs the Pay-as-you-go plan, $20 a month plus usage (ngrok pricing↗).
What does my app see behind Funnel?
Funnel forwards the request with these headers (client IP redacted):
{
"host": "bench-2.<tailnet>.ts.net",
"user-agent": "curl/8.15.0",
"accept": "*/*",
"tailscale-funnel-request": "?1",
"x-forwarded-for": "<redacted>",
"x-forwarded-host": "bench-2.<tailnet>.ts.net",
"x-forwarded-proto": "https",
"accept-encoding": "gzip"
}
Your device's IP address stays out of public DNS. The ts.net name resolves to Tailscale's relay addresses, and Tailscale says the proxy "lets Tailscale hide the IP address of your local device" (how Funnel works↗). The visitor's IP reaches your app in x-forwarded-for.
LocalCan sends the same x-forwarded-proto: https and x-forwarded-host, plus x-forwarded-port. Both end HTTPS before your app (Funnel on your device, LocalCan at its edge) and send it plain HTTP. If your app redirects to HTTPS in a loop behind either one, check that it trusts the proxy's forwarded headers. A Symfony app behind Funnel↗ needed two Caddy changes and Symfony's trusted_proxies setting before its redirect loops stopped.
LocalCan also keeps the last 250 requests in its traffic inspector, where you can replay one or copy it as cURL. Funnel's documentation describes no request log.
Tailscale Funnel vs LocalCan
| Tailscale Funnel | LocalCan | |
|---|---|---|
| Public address | <device>.<tailnet>.ts.net | <name>.localcan.dev (trylocalcan.com on Free), or your own domain on paid plans |
| Public URLs | One name per device, on ports 443, 8443 and 10000 | One or more per service, 5 live on Solo, 10 on Pro |
| Forwards to | Services on 127.0.0.1 | localhost or another host, with --host |
| New URL to first response (measured) | Under 2 to 10 minutes, 3 runs | 0.7 to 1.5 s, 6 runs |
| 100 MB download (measured median) | 7.0 Mbps | 56.6 Mbps |
| Where TLS ends | On your device, the relay can't read traffic | On LocalCan's edge |
| Request inspector and replay | Not in Funnel's documentation | Built in |
| Password on the URL | Not in Funnel's documentation | Access control on paid subscription plans, Basic auth on every plan |
| TCP | TLS-wrapped TCP only, on every plan, ports 443, 8443 and 10000 | Paid plans |
| Free plan | Personal, non-commercial use only | 1 Public URL, 60-minute sessions, 1 GB a month |
| Paid from | $8 per user per month (Standard) | $10 a month, or $96 a year (Solo) |
| Client source | Mostly open source: BSD 3-Clause daemon and CLI, closed GUI on macOS, Windows and iOS | Closed source |
Funnel wins on four rows. TLS ends on your own device, TLS-wrapped TCP costs nothing, the core client is open source↗, and Tailscale lists no session length for Funnel on the Personal plan.
How does Funnel compare with Cloudflare Tunnel and ngrok?
Cloudflare Tunnel is available on every Cloudflare plan↗, the free one included, and lifts Funnel's naming limits. A named tunnel serves any hostname on a domain you keep on Cloudflare, and a quick tunnel needs no account, with a trycloudflare.com name that changes every run (quick tunnels↗). ngrok's free plan gives you one dev domain on ngrok-free.app and 1 GB of transfer a month, and shows browser visitors a warning page before your app (ngrok free plan limits↗).
When is Funnel the better choice?
When the URL serves something you run for yourself on a machine already in your tailnet: a home server, a webhook endpoint you set up once for a side project, or a file handed out with tailscale funnel /path/to/file (on macOS, file sharing needs the open source client). It costs nothing on the Personal plan, Tailscale's relay can't read the traffic, and you can switch to tailscale serve to share it only with your own devices.
A tunnel service fits when the link goes to someone outside your tailnet: a client, a teammate, or a webhook provider whose payloads you want to see and replay. For the other two common choices, see LocalCan as a Cloudflare Tunnel alternative and LocalCan as an ngrok alternative. The ngrok alternatives compared post sets the free tiers of six alternatives side by side.
