Expose localhost to the internet. Single binary, no account required.
The public server at tnnl.run is free to use:
curl -fsSL https://tnnl.run/install.sh | sh
tnnl http 3000
# → https://abc12345.tnnl.run is liveEvery request is logged as it comes in:
00:05 #1 GET / 200 16ms
00:09 #2 GET /api/users 200 20ms
00:14 #3 POST /api/webhooks 201 14ms
One-line (Linux / macOS):
curl -fsSL https://tnnl.run/install.sh | shPrebuilt binary: releases page
Via cargo:
cargo install tnnl-cliFrom source:
cargo install --path .The main reason to reach for tnnl over a quick SSH tunnel. Pass --inspect to see full headers and body for every request and response:
tnnl http 3000 --inspect00:14 #3 POST /api/webhooks 201 14ms
→ POST /api/webhooks #3
Content-Type: application/json
Stripe-Signature: t=1234567890,v1=abc123...
{
"type": "charge.succeeded",
"data": { "amount": 9900, "currency": "usd" }
}
← 201 Created
Content-Type: application/json
{"received": true}
Every request is saved locally. Fix your handler and replay without touching the sender:
tnnl replay 3IDs persist across restarts so you can replay old requests after a fresh start.
Persistent settings live in ~/.tnnl.toml:
server = "tnnl.run"
token = "your-secret" # omit for open servers
subdomain = "myapp" # same URL every time
auth = "user:pass" # HTTP basic auth on the tunnelCLI flags always win over the config file.
Gate the exposed URL with HTTP basic auth. Unauthenticated requests are rejected before they touch your local service:
tnnl http 3000 --auth user:passOne binary, two modes. tnnl server runs on a VPS, tnnl http runs on your machine.
- Client connects over TCP and authenticates with HMAC-SHA256 - the secret never crosses the wire
- Server assigns a subdomain (random, or pin one with
--subdomain) - Incoming HTTP traffic is routed by
Hostheader to the right client - Everything flows over a single multiplexed connection via yamux - no TCP handshake per request
- Client proxies to localhost and pipes the response back
502 if localhost isn't up. Exponential backoff reconnect if the connection drops.
tnnl server --domain tunnel.example.com
# or with a shared secret
tnnl server --domain tunnel.example.com --token supersecretOmitting --token makes it an open server. Either way, abuse protection is always on: per-IP rate limiting, per-IP tunnel caps, global tunnel limit.
| Flag | Default | Description |
|---|---|---|
--domain |
required | Base domain for tunnel subdomains |
--token |
none | Secret clients must know - omit for open server |
--control-port |
9443 |
Port for client connections |
--http-port |
8080 |
Port for public HTTP traffic |
tnnl terminates plain HTTP on --http-port. Put Caddy or nginx in front for TLS with a wildcard cert.
Caddy:
*.tunnel.example.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
reverse_proxy localhost:8080
}
nginx:
server {
listen 443 ssl;
server_name *.tunnel.example.com;
ssl_certificate /etc/letsencrypt/live/tunnel.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tunnel.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}DNS: wildcard A record pointing to your VPS:
*.tunnel.example.com. A <your-vps-ip>
| Flag | Default | Description |
|---|---|---|
--to |
tnnl.run |
Server address |
--token |
config / none | Shared secret for authentication |
--subdomain |
random | Request a specific subdomain |
--auth |
none | Protect tunnel with HTTP basic auth (user:pass) |
--control-port |
9443 |
Server control port |
--inspect |
off | Print full request/response headers and body |
Re-send a captured request to your local server. IDs are shown in the request log.
See Self-hosting above.
| tnnl | ngrok | bore | frp | |
|---|---|---|---|---|
| Self-hosted | ✓ | ✗ | ✓ | ✓ |
| No account required | ✓ | ✗ | ✓ | ✓ |
| Public shared server | ✓ | ✓ | ✓ | ✗ |
| Single binary | ✓ | ✓ | ✓ | ✗ |
| HTTP subdomain routing | ✓ | ✓ | ✗ | ✓ |
| Auth | HMAC | HMAC | HMAC | token |
| Config file | ✓ | ✓ | ✗ | required |
| Auto-reconnect | ✓ | ✓ | ✗ | ✓ |
| Request inspection | ✓ | ✓ | ✗ | ✗ |
| Replay | ✓ | ✓ | ✗ | ✗ |
| Tunnel basic auth | ✓ | paid | ✗ | ✗ |
| Free | ✓ | limited | ✓ | ✓ |
MIT
