
The short version
You can serve a site from a spare machine at home without opening a single router port, by using a tunnel that your machine opens to a provider. They differ in whose domain you use, what the free tier allows, and what each one was built for.
Cloudflare Tunnel is the pick for a site you want to keep running under your own domain.
Tailscale Funnel is the fastest to set up if you already use Tailscale: one command gives you a public HTTPS address with no domain to buy, but it has tight limits on ports and bandwidth, and it is still in beta.
ngrok is built for development and demos. It has the best request inspector of the three, but its free plan has small monthly quotas.
The rest of this post explains how each works, where each one breaks, and how to choose. Limits and pricing change often, so the figures here are as of October 2026 and link to their sources at the end.
How all three work
All three swap a port-forwarding problem for an outbound-connection problem. A small agent on your machine opens a connection to the provider, and the provider sends each visitor's request down it and returns the response the same way, so your router never accepts a connection from outside. Cloudflare notes that this works without a publicly routable IP address, which helps when your internet provider shares one address across many customers.
flowchart LR
V["Visitor<br/>with a browser"] -- request --> P["Provider's edge<br/>Cloudflare, Tailscale relay or ngrok"]
P <==>|"one connection, opened outbound<br/>from your machine"| A
subgraph Home["Your home network: no inbound port open"]
A["Tunnel agent<br/>cloudflared, Tailscale or the ngrok agent"] --> App["Your app<br/>listens on localhost only"]
endCloudflare Tunnel: your domain, always on
Cloudflare Tunnel is the one to use for a site that should stay up, because it puts a hostname on your own domain in front of your home server and keeps it there. A program called cloudflared opens outbound connections from your machine to the nearest Cloudflare data center, and Cloudflare's docs note that you can then block all inbound traffic at your firewall.
The catch comes first: to publish an application through a tunnel, you must add a domain to Cloudflare. If you do not own one yet, a quick tunnel works for testing:
cloudflared tunnel --url http://localhost:8080That prints a random trycloudflare.com address and needs no account or domain, but the address changes every time. Cloudflare says quick tunnels are for testing, have no uptime guarantee, allow 200 in-flight requests per tunnel (more get a 429), and do not support Server-Sent Events.
Setting up a permanent tunnel
There are two ways to do it:
In the dashboard. Go to Networking, then Tunnels, and create a tunnel. Copy the install command it generates for your operating system and run it on the server. Then add a published application route: choose a subdomain and domain, and set the service URL, such as
http://localhost:8000.With a config file. Keep the rules in
config.ymland run the tunnel from it:
tunnel: <Tunnel-UUID>
credentials-file: /root/.cloudflared/<Tunnel-UUID>.json
ingress:
- hostname: example.com
service: http://localhost:8000
- service: http_status:404The final http_status:404 rule is a required catch-all. Two commands help while you write the file: cloudflared tunnel ingress validate checks the rules, and cloudflared tunnel ingress rule https://example.com shows which rule a URL matches.
To create and run the same tunnel from the command line, these four commands are enough:
cloudflared tunnel login
cloudflared tunnel create home
cloudflared tunnel route dns home app.example.com
cloudflared tunnel run homeIf your config file is elsewhere or has a custom name, put --config /path/your-config-file.yml before run.
What you get
No charge for the tunnel. Cloudflare announced in 2021 that any organization can use the outbound-only connection at no cost. Check current terms before you depend on it.
More than websites. Tunnel can also connect SSH servers, remote desktops and other protocols.
Failover. You can run as many
cloudflaredconnectors as you like in one tunnel, so a second machine at home can take over.A login in front. Cloudflare Access applications are deny-by-default, so you add an Allow policy for the people you trust. The Security section covers the order to set it up in.
Where it falls short
Uploads are capped. Requests through Cloudflare's proxy are limited to 100 MB on the Free and Pro plans and 200 MB on Business.
Media rules. Cloudflare's CDN terms still restrict video and large files hosted outside Cloudflare. The announcement does not say whether that covers Tunnel, so read the current terms before you stream media from home.
Tailscale Funnel: the fastest start, with the tightest rules
If you already run Tailscale, Funnel is the quickest way to make one service public: one command gives you an HTTPS address, with nothing to install beyond Tailscale. Tailscale describes it as routing traffic from the public internet to a service on a device in your tailnet, through an encrypted TCP proxy on Funnel relay servers that cannot decrypt the traffic.
Setting it up
You need Tailscale v1.38.3 or later, MagicDNS, HTTPS certificates enabled for the tailnet, and the funnel node attribute in your policy file (by default it goes to autogroup:member). The first time you run tailscale funnel, it opens a web page where you approve Funnel, and Tailscale then creates the certificates and adds the attribute for you.
# Expose the app on local port 3000 and keep it running after reboots
tailscale funnel --bg 3000
# See what is currently public
tailscale funnel status
# Turn everything off
tailscale funnel resetThe --bg flag matters. Without it, Funnel stops when you reboot or restart Tailscale and you have to start it again by hand. With it, Funnel resumes on its own.
What you get
No domain to buy. Your address is a name under your tailnet's
ts.netdomain, and nothing else is needed.A private mode on the same tool.
tailscale serveshares a service only with devices in your tailnet, while Funnel makes it public. A port cannot be both at once: the most recentserveorfunnelcommand on it decides.Every plan. Funnel is available on all Tailscale plans.
Where it falls short
Three ports only. Funnel can listen only on 443, 8443 and 10000.
TLS only. Funnel works only over TLS-encrypted connections, so it is not a way to expose a plain TCP service.
Beta. Tailscale still labels Funnel as beta, so behavior and limits may change.
Bandwidth limits you cannot tune. Traffic is subject to non-configurable bandwidth limits, and the docs give no figures. Test it with your real traffic before you rely on it, especially if you serve large files.
Your names only. Funnel can use only names in your tailnet's domain, so your own domain is not an option.
ngrok: built for development, usable for hosting
ngrok is the tool here that is best at inspecting and shaping traffic, and its free plan is sized for testing rather than for running a site. Getting started takes one command:
ngrok http 8080Every account gets a free dev domain. Per ngrok's docs, "its URL is automatically generated and cannot be changed," and it is a stable, auto-assigned hostname. Choosing your own name with the --url flag is a paid-plan feature.
What the plans give you
Free | Hobbyist ($10 a month) | |
|---|---|---|
Domains | 1 auto-assigned dev domain | 10 ngrok-branded domains |
Data transfer out | 1 GB a month | 5 GB a month, then $0.10 per GB |
HTTP requests | 20,000 a month | 100,000 a month, then $1 per 100,000 |
Interstitial warning page | Shown to browsers | Removed |
Online endpoints | 3 | 3 |
Traffic Inspector history | 24 hours | 72 hours |
Bringing your own custom domain starts at the Pay-as-you-go plan, which is $20 a month plus usage. The free-plan figures come from ngrok's limits page.
The interstitial page
On the free plan, ngrok shows a warning page before HTML traffic from a browser, to deter phishing. A visitor who clicks through gets a cookie that hides the page for that domain for 7 days. It does not affect API or programmatic clients, and you can skip it by sending an ngrok-skip-browser-warning header with any value.
Running it from a config file
Open the config with ngrok config edit, which creates ngrok.yml when you add your authtoken. A minimal v3 file looks like this:
version: 3
agent:
authtoken: YOUR_AUTHTOKEN
endpoints:
- name: home
url: your-assigned-name.ngrok-free.app
upstream:
url: 8080Then ngrok start home runs it in the foreground.
To keep it running without a terminal open, install the agent as an operating system service:
ngrok service install --config=/etc/ngrok/ngrok.yml
ngrok service startThe service behaves as if you had run ngrok start --all, so your config file has to define the endpoints. The docs do not say whether the service starts at boot, so reboot the machine once and check.
Where it earns its place
Seeing every request. The Traffic Inspector shows requests and responses, which is why ngrok is so good for webhook work.
Rules in front of your app. Traffic Policy lets you add checks such as OAuth or webhook verification before a request reaches you. The free plan allows 5 rules per policy and 3 monthly active users on OAuth and OIDC logins.
Where it falls short
Small quotas. A page with a few images can use up the monthly data allowance quickly, and the request allowance is small for anything public.
A warning page for browsers. The free-plan interstitial described above is a real annoyance on a website your friends open in a browser, and only a paid plan removes it.
Side by side
The three differ most in who owns the address and how much traffic they allow.
ngrok (Free plan) | |||
|---|---|---|---|
Your address | Your own domain; quick tunnels get a random | A name under your tailnet's | An auto-assigned dev domain that cannot be changed |
Domain you must own | Yes, added to Cloudflare, for a permanent tunnel | No | No |
What it carries | HTTP sites, plus SSH, remote desktop and other protocols | TLS-encrypted connections on ports 443, 8443 and 10000 | HTTP endpoints; raw TLS endpoints need a paid plan |
Traffic limits | 100 MB per upload on Free and Pro zones; no monthly quota stated | Bandwidth limits you cannot change, with no figures published | 1 GB out and 20,000 HTTP requests a month |
Maturity | Quick tunnels have no uptime guarantee; permanent tunnels are the production path | Beta | Free plan shows a warning page to browsers |
Security: the tunnel is your new front door
A tunnel removes your router as a barrier, so anything you publish is reachable from the whole internet and needs its own login before you point a hostname at it.
Put a login in front first. With Cloudflare, create the Access application before the tunnel route, because a route without one is open to anyone, and turn on Protect with Access so
cloudflaredrejects requests that bypass Access. With ngrok, add OAuth through Traffic Policy. Tailscale's Funnel page describes no login layer, so the app has to handle authentication itself, or you can usetailscale serveto keep the service private to your tailnet.Bind the app to localhost. If the app listens only on
127.0.0.1, other devices on your home network cannot reach it directly, and the tunnel agent is the only way in.Keep the machine small. Use a dedicated machine, a VM or a container, run the app as a non-root user, turn on automatic security updates, and keep nothing sensitive beside it.
Assume the provider can read HTTP traffic, except on Funnel. Cloudflare Access and ngrok's Traffic Policy and inspector work on the contents of HTTP requests, which means that traffic is visible to them. Tailscale says Funnel relays cannot decrypt what they carry. Either way, keep secrets out of URLs and logs.
Know your off switch.
tailscale funnel resetclears Funnel,ngrok service stopstops the ngrok service, and stopping thecloudflaredprocess closes the tunnel. Try it once before you need it.
Which one should you pick?
Start with what the thing is for, then ask about your domain, and only then check whether Funnel's rules fit.
flowchart TD
Q1{"Is it a demo, a webhook test<br/>or something you are still building?"}
Q1 -- yes --> R1["ngrok<br/>Request inspector and replay, free dev domain"]
Q1 -- no --> Q2{"Do you want visitors to see<br/>your own domain in the address?"}
Q2 -- yes --> R2["Cloudflare Tunnel<br/>Your domain, Access login in front, no charge for the tunnel"]
Q2 -- no --> Q3{"Do you already use Tailscale and need<br/>only TLS on 443, 8443 or 10000?"}
Q3 -- yes --> R3["Tailscale Funnel<br/>One command, no domain to buy, beta, with limits you cannot tune"]
Q3 -- no --> R4["Buy a domain, use Cloudflare Tunnel<br/>or move to a paid ngrok plan"]The order matters because the free plans fail in different places. ngrok's quotas and warning page get in the way of a public site first, Cloudflare's domain requirement gets in the way if you do not own a domain, and Funnel's ports and bandwidth limits get in the way of anything heavy. If two answers fit, choose the one whose limit you can live with.
Sources
All pages were opened in October 2026.
Cloudflare
Goodbye, section 2.8, and hello to Cloudflare's new terms of service
Cloudflare Tunnel for everyone (April 2021)
Tailscale
ngrok
Comments(0)
Join the discussion by logging into your account.
No comments yet. Be the first to comment!