{"slug":"open-source-ngrok-alternative-local-https-dev-tunnels","url":"https://tako.sh/blog/open-source-ngrok-alternative-local-https-dev-tunnels/","canonical":"https://tako.sh/blog/open-source-ngrok-alternative-local-https-dev-tunnels/","title":"The Open Source ngrok Alternative for Local HTTPS Dev Tunnels","date":"2026-06-21T15:02","description":"Use tako dev --tunnel for public HTTPS dev URLs with stable hostnames, signed identity, reconnects, and no separate tunnel daemon.","author":null,"image":"675b059cfc16","imageAlt":null,"headings":[{"depth":2,"slug":"the-local-tunnel-problem","text":"The Local Tunnel Problem"},{"depth":2,"slug":"what-makes-tako-different","text":"What Makes Tako Different"},{"depth":2,"slug":"a-respectful-comparison","text":"A Respectful Comparison"},{"depth":2,"slug":"how-it-behaves-when-things-get-weird","text":"How It Behaves When Things Get Weird"},{"depth":2,"slug":"try-it","text":"Try It"}],"markdown":"There are two moments when local development suddenly needs a public URL.\n\nThe first is practical: a webhook provider, OAuth callback, teammate, or phone on another network needs to reach the app on your laptop. The second is emotional: you realize the clean `https://my-app.test/` setup you had five minutes ago is about to become three terminals, a random tunnel URL, and a sticky note about callback URLs.\n\nThat second moment is why open-source [Tako](https://github.com/tako-sh/tako) has [`tako dev --tunnel`](/docs/development/): a public HTTPS URL for a running app without a separate tunnel daemon.\n\n```bash\ntako dev --tunnel\n```\n\nOr, if `tako dev` is already running, press `t`.\n\nTako creates a public URL like:\n\n```text\nhttps://my-app-k7q4z2.tako.website/\n```\n\nThe URL is public, HTTPS, and stable for the same app name plus local Tako Identity. When the tunnel connection drops, Tako keeps the URL reserved, shows it as reconnecting, and retries automatically. You do not need a second process, a reserved subdomain, or a config file just to remember where your app is running.\n\n## The Local Tunnel Problem\n\nTools like [ngrok](https://ngrok.com/), [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), [localtunnel](https://github.com/localtunnel/localtunnel), [zrok](https://github.com/openziti/zrok), and [bore](https://github.com/ekzhang/bore) are useful. They all solve a real networking problem: your laptop is behind NAT, the internet cannot dial it directly, so an outbound connection has to meet a relay somewhere public.\n\nThe awkward part is that most tunnel tools sit beside your dev environment instead of inside it. Your app runs on one local port. Your framework has its own allowed-host rules. Your tunnel tool forwards to whatever port you remembered to type. If the public hostname changes, callbacks need updates. If the tunnel process dies, your app may still be fine locally, but everyone else sees a dead URL.\n\nThat shape is okay for one-off demos. It gets annoying when public dev URLs become part of your daily loop.\n\nTako already owns the local dev surface: the [dev daemon](/blog/the-dev-daemon-tako-dev-is-just-a-client/) starts the app, assigns the loopback port, terminates local HTTPS, tracks registered routes, streams logs, and keeps `https://my-app.test/` alive. Tunnel mode plugs into that same system instead of asking you to bolt another one on.\n\n```d2\ndirection: right\n\nbrowser: \"teammate / webhook\" {\n  shape: rectangle\n}\n\ntunnel: \"tako.website tunnel service\" {\n  shape: cloud\n  style.fill: \"#9BC4B6\"\n}\n\ndaemon: \"tako-dev-server\" {\n  style.fill: \"#E88783\"\n}\n\nproxy: \"local HTTPS proxy\" {\n  style.fill: \"#FFF9F4\"\n}\n\napp: \"your app\" {\n  shape: hexagon\n}\n\nidentity: \"Tako Identity\" {\n  shape: cylinder\n}\n\nbrowser -> tunnel: \"https://my-app-id.tako.website\"\ndaemon -> tunnel: \"signed outbound WebSocket\"\nidentity -> daemon: \"sign challenge\"\ntunnel -> daemon: \"HTTP / WebSocket frames\"\ndaemon -> proxy: \"Host: my-app.test\"\nproxy -> app: \"loopback port\"\n```\n\nThe public side speaks to `tako.website`. The local side stays routed through the same dev proxy that already serves your `.test` URL.\n\n## What Makes Tako Different\n\nThe big difference is not that Tako has invented tunneling. It is that the tunnel knows about your app.\n\nWhen you enable tunnel mode, the dev server loads or creates a local Tako Identity, asks the tunnel service for a challenge, signs that challenge, and opens an outbound WebSocket session. The public hostname includes the app name and an id derived from the app plus identity, so the same project on the same machine gets the same public URL when the identity is available.\n\nOn macOS, Tako tries to keep that identity in iCloud Keychain and falls back to local storage when synced Keychain access is unavailable. On other platforms, it uses local identity storage. Either way, the URL is tied to a local key, not a random string printed by a throwaway process.\n\nThat gives tunnel mode a few nice properties:\n\n| Concern            | Separate tunnel CLI                            | `tako dev --tunnel`                                                |\n| ------------------ | ---------------------------------------------- | ------------------------------------------------------------------ |\n| Local app target   | Usually a port you pass by hand                | The app registered with the Tako dev daemon                        |\n| Public URL         | Often random unless reserved/configured        | Stable for app name plus Tako Identity                             |\n| HTTPS              | Public HTTPS from the tunnel provider          | Public HTTPS plus local HTTPS through Tako                         |\n| Host routing       | You wire Host headers and framework allowlists | Tako routes through the existing dev route                         |\n| Process model      | Extra daemon/process beside your app           | Built into the running `tako dev` session                          |\n| Reconnect behavior | Depends on the tool and how it is supervised   | URL stays reserved while Tako reconnects                           |\n| Local docs path    | Tool-specific setup                            | Same [`tako dev`](/docs/development/) and [`CLI`](/docs/cli/) flow |\n\nThe result feels smaller. You run the same command you were already running:\n\n```bash\ntako dev --tunnel\n```\n\nYou can still use the local URL:\n\n```text\nhttps://my-app.test/\n```\n\nAnd now you also have the public one:\n\n```text\nhttps://my-app-k7q4z2.tako.website/\n```\n\nBoth point at the same registered app. Both flow through the same route model described in the [`tako.toml` reference](/docs/tako-toml/). The public URL is just the internet-facing entrance.\n\n## A Respectful Comparison\n\nIf you need a general-purpose tunnel, the standalone tools are still good tools. Ngrok has a mature hosted platform and domain controls. Cloudflare Tunnel is excellent when you already own the domain and want traffic on Cloudflare's network. zrok is a serious open-source sharing system built around zero-trust networking. localtunnel is simple for quick Node-flavored sharing. bore is tiny and TCP-shaped.\n\nTako is narrower on purpose: local HTTPS dev tunnels for apps already running under Tako.\n\n| Tool              | Best fit                                                           | Tradeoff for local app dev                                                    |\n| ----------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------- |\n| ngrok             | Polished hosted tunnels, traffic features, managed domains         | Another CLI and account surface outside your dev runtime                      |\n| Cloudflare Tunnel | Stable custom hostnames on Cloudflare-controlled DNS               | Great but setup-heavy for quick branch previews                               |\n| localtunnel       | Very quick open-source localhost sharing                           | Public hostname availability and HTTPS-to-local details are separate concerns |\n| zrok              | Open-source sharing with private/public modes and zero-trust roots | More general than a framework-aware dev command                               |\n| bore              | Small self-hostable TCP tunnels                                    | Lower-level than app-aware HTTPS routing                                      |\n| Tako tunnel       | Public URL for a `tako dev` app                                    | Only useful when the app is running through Tako                              |\n\nThat last row is the point. We are not trying to replace every tunnel tool. We are trying to remove a whole category of tunnel setup from the path between \"my app works locally\" and \"someone else can open it.\"\n\nFor webhook testing, this means you can give Stripe, GitHub, Customer.io, or any other callback sender a stable app-derived URL while your local app keeps running behind Tako. For OAuth, it means fewer \"which callback URL is live today?\" tabs. For teammate review, it means `tako dev --tunnel`, paste, keep coding.\n\n## How It Behaves When Things Get Weird\n\nTunnel mode is deliberately boring during failure. That is a compliment.\n\nIf the tunnel connection drops, the URL does not disappear. Tako keeps tunnel mode enabled, marks the tunnel as reconnecting in the status panel, retries with bounded exponential backoff, and prints log lines when reconnecting starts and when the tunnel reconnects.\n\nIf you start another tunnel for the same app and identity, it replaces the previous active session for that URL. If one identity has too many active tunnel URLs at once, the service accepts the new one and closes the oldest active tunnel for that identity. The closed client turns tunnel mode off and tells you why.\n\nIf someone visits a tunnel URL while it is inactive or disconnected, they get a Tako-styled error page in the browser. API-ish clients that send `Accept: application/json` get JSON, and other clients get plain text.\n\nThat behavior matters because dev tunnels are usually used right when someone else is waiting. A webhook retry is hitting your endpoint. A teammate is refreshing a bug repro. An OAuth provider is redirecting back to you. The URL should be the stable thing, even if the laptop briefly changes Wi-Fi networks.\n\n## Try It\n\nInstall Tako, run your app through [`tako dev`](/docs/development/), and turn on the tunnel:\n\n```bash\nbrew install takoserver/tap/tako\ncd your-project\ntako dev --tunnel\n```\n\nInside an existing interactive session, press `t` to toggle the tunnel. Run `tako dev list` to see registered apps and the current tunnel URL for any app with tunnel mode enabled.\n\nFor local-device testing on the same Wi-Fi, use [LAN mode](/blog/lan-mode-hand-your-app-to-a-phone/) instead. For a custom domain on Cloudflare, the manual [Cloudflare Tunnel setup](/blog/cloudflare-tunnel-vite-local-https-dev/) still works. For the common \"I need a public HTTPS URL for this local Tako app right now\" case, tunnel mode is the shorter path.\n\nNo port forwarding. No second tunnel daemon. No random URL musical chairs. Just your app, already running under Tako, with a public HTTPS door when you need one."}