Quick tunnels


Open a tunnel to a local port without writing any config — three subcommands cover HTTP, HTTPS, and raw TCP.

Quick tunnels are designed for "I just want to share this for a minute" — you run one command, you get a public URL, you hit Ctrl+C and it's gone. They auto-spawn the daemon if it isn't running, and they don't load any of your saved projects, so they're safe to use alongside a long-running setup.

HTTP

Shell
localcan http 3000

Opens a tunnel to http://localhost:3000. LocalCan generates a random .local name (e.g. swift-falcon.local) and a Public URL on the LocalCan.dev domain. The .local name is served over HTTPS, and http://swift-falcon.local redirects to it. LocalCan turns on the local HTTP and HTTPS proxy automatically if it is not already running.

Without a license (free mode), localcan http still works but only the .local URL is published. The command prints a notice and skips the Public URL. Run localcan license activate <key> to enable Public URLs.

If you have a custom domain verified for your account, pin the tunnel to it with --url (handy for OAuth callbacks and other URLs you can't change between runs):

Shell
localcan http 3000 --url preview.example.com

--url only accepts a custom domain you own — random *.localcan.dev subdomains can't be requested by name, the backend assigns them.

While the tunnel is open, the live display shows the connecting status, the local URLs and the Public URL, and incoming requests with timestamp, method, status, and path:

Text
Connecting tunnel to http://localhost:3000...
Forwarding https://swift-falcon.local → localhost:3000
Forwarding http://swift-falcon.local → localhost:3000
Forwarding https://swift-falcon-12.localcan.dev → localhost:3000

Requests (Ctrl+C to stop):

15:04:21    GET 200  /
15:04:21    GET 200  /_next/static/css/app.css
15:04:23   POST 201  /api/orders

The request view holds the most recent ten and updates in place — no scrollback noise.

HTTPS

Shell
localcan https 8443

Same as http, but for local apps that already terminate TLS. LocalCan trusts your local server's certificate even if it's self-signed. The live display is identical to HTTP. In free mode the .local URL is published the same way as for localcan http, with no Public URL.

TCP

Shell
localcan tcp 5432

Opens a raw TCP tunnel — useful for databases, SSH, and other non-HTTP services. The Public URL takes the form tcp://*.localcan.dev:<port>. --url is not supported for TCP tunnels.

TCP tunnels require a license. In free mode localcan tcp exits with an error (there's no .local fallback for raw TCP, since Bonjour/mDNS publishes only the HTTP/HTTPS proxy).

The live display shows active and total connection counts:

Text
Connections (Ctrl+C to stop):

  Active: 2    Total: 17

Scripting with --json

For pipelines and AI agents, add --json to stream the tunnel as newline-delimited JSON, one event per line on stdout. Human status messages stay on stderr, so stdout is clean to pipe into jq. The flag works for http, https, and tcp.

The one-shot commands like localcan tunnel ls --json print a single JSON document. Quick tunnels are long-running, so they emit a stream instead:

Text
{"event":"forwarding","time":"2026-05-20T15:04:18+02:00","scheme":"https","url":"swift-falcon.local","target":"localhost:3000"}
{"event":"forwarding","time":"2026-05-20T15:04:18+02:00","scheme":"http","url":"swift-falcon.local","target":"localhost:3000"}
{"event":"forwarding","time":"2026-05-20T15:04:19+02:00","scheme":"https","url":"swift-falcon-12.localcan.dev","target":"localhost:3000"}
{"event":"request","time":"2026-05-20T15:04:21+02:00","method":"GET","status":200,"path":"/","duration_ms":42,"response_bytes":1530}

Every line carries an event field and a time (RFC3339). For request events time is when the request was captured. For forwarding and connections events it is when the line was emitted. forwarding events announce the local and Public URLs as they come up. request events report one completed request each, with status, duration, and response size folded in (so no cross-line correlation is needed). response_bytes is the full response body size on the wire. TCP tunnels emit connections events with active and total counts instead of requests.

When no Public URL is streamed, a status event says why rather than leaving you to infer it: free_mode (no license, only the .local URL is served) or url_pending (the public tunnel did not return a URL within the connect window). Fatal errors still go to stderr with a non-zero exit.

Filter the stream with jq:

Shell
localcan http 3000 --json | jq -c 'select(.event=="request")'

Fatal errors stay on stderr and the command exits non-zero, so a failed tunnel is detectable without parsing the stream.

What gets created

Each invocation writes a temporary project file at ~/.localcan/projects/_quick_<scheme>_<port>_<random>.yml and tells the daemon to reload. For http and https tunnels it also enables the local HTTP and HTTPS proxy listeners so the .local URL is reachable on both schemes. When you Ctrl+C, the file is removed and the daemon reloads again (the proxy listeners stay enabled).

If both the localcan quick-tunnel process and its daemon exit abnormally, the next quick-tunnel invocation spawns a fresh daemon and sweeps any leftover _quick_*.yml files. If only the quick-tunnel process exits but the daemon keeps running, the orphan file stays in projects/ until you remove it (or until the daemon is stopped and a new quick tunnel is run).

Because quick tunnels live in projects/, they show up in localcan projects ls and localcan tunnel ls while running.

When to use a saved project instead

Quick tunnels are great for one-offs. For anything you'll restart often — a permanent dev domain, a tunnel with custom headers, a setup with multiple services — write a YAML project file and use localcan reload. See Configuration → Projects.

© 2026 LocalCan™. All rights reserved.