Skip to content

Create a tunnel

The CLI is the main way to run a tunnel. It creates the local↔public forwarding session and prints your URL.

Terminal window
npx @maxoperf/tunnel http 3000
✔ guest tunnel online https://swift-otter-7f3.maxoperf-tunnel.com → http://127.0.0.1:3000
↑ sign up for a named subdomain, more tunnels, access controls & capture: https://maxoperf.com/signup
12:00:01 GET /api/users 200 12ms 1.2kB

No token, no account. The CLI saves a durable guest identity to ~/.maxoperf/tunnel.json (file mode 0600), so re-running the command reuses the same URL.

Flag / envPurpose
--token <mptt_…> / MAXOPERF_TUNNEL_TOKENTunnel client token. Omit for a guest tunnel.
--name <name>Preferred subdomain label (informational only; the server sets the URL at create time).
--server <host:port> / MAXOPERF_TUNNEL_SERVERGateway address (default app.maxoperf.com:443, TLS auto-detected).
--api-url <url> / MAXOPERF_API_URLPlatform API origin, used for guest tunnels (default https://app.maxoperf.com).
--local-host <host>Local host to forward to (default 127.0.0.1).

Credential resolution order: --token → MAXOPERF_TUNNEL_TOKEN → the saved config-file account token → the saved config-file guest token → mint a brand-new guest. Ctrl-C closes the session cleanly, so the tunnel goes offline at once instead of lingering as a zombie session. The CLI never prints or logs your token.

  1. Open Tunnels in the left navigation and click Create tunnel.
  2. Enter a name: the DNS label that becomes <name>.maxoperf-tunnel.com (lowercase letters, digits, hyphens; must be globally unique).
  3. Optionally configure access controls (below), then save. MaxoPerf creates the tunnel offline. It goes live as soon as a client (the CLI, or your own gRPC client) connects with its token.
  4. Reveal the tunnel’s connection token from the tunnel detail page and run the CLI (or your client) with it.
Tunnels list. Each row shows the tunnel's URL and whether a client is connected right now (online).
Tunnel detail, Overview tab: status, URL, and live traffic statistics.
Terminal window
curl -X POST https://app.maxoperf.com/v1/tunnels \
-H "Authorization: Bearer mpak_example_key" \
-H "X-Account-Id: acn-1234567890" \
-H "Content-Type: application/json" \
-d '{
"name": "myapp",
"authMode": "basic",
"basicAuth": { "username": "demo", "password": "s3cret" },
"allowIps": ["203.0.113.0/24"],
"rateLimitRpm": 600,
"maxBodyBytes": 10485760,
"idleTimeoutSeconds": 3600,
"captureMode": "headers"
}'

The 201 response returns the tunnel’s URL and a one-time clientToken (mptt_…). MaxoPerf never shows it again, so copy it now. Other routes: GET /v1/tunnels, GET /v1/tunnels/:id, PATCH /v1/tunnels/:id (any config field except name), POST /v1/tunnels/:id/stop, POST /v1/tunnels/:id/start, DELETE /v1/tunnels/:id (stopped only), POST /v1/tunnels/:id/rotate-token.

Configure these at create time or later via PATCH, from the tunnel’s Settings tab in the console:

ControlWhat it does
Auth mode: none / basic / bearerRequire HTTP basic auth or a bearer token before a request reaches your local server.
Allow / deny IPsCIDR lists. An IP not in allowIps (when set) is blocked; an IP in denyIps is always blocked.
Rate limit (rateLimitRpm)Requests per minute before the tunnel starts responding 429.
Max body size (maxBodyBytes)Caps request/response body size the tunnel will proxy.
Idle timeout (idleTimeoutSeconds)How long the tunnel can sit with no traffic before it auto-stops. Default 3600s (1 hour).
Expiry (expiresAt)Optional absolute ISO-8601 timestamp after which the tunnel stops permanently.
  • Idle or TTL auto-stop reactivates on reconnect. If a tunnel stopped itself because it went idle or hit its expiry, a client that reconnects with a valid token brings it straight back online on the same URL. You need no separate “start” step. This is why the guest URL stays the same across restarts.
  • A manual or plan-driven stop does not. If you (or MaxoPerf, for a plan-limit reason) stopped a tunnel on purpose, it stays stopped even if a client keeps retrying. Call POST /v1/tunnels/:id/start (or use the console) to bring it back. The rule exists so that a client’s automatic reconnect can’t quietly get around a deliberate stop or a plan cap.
  • Dropped network ≠ stopped tunnel. If your local network drops briefly, the CLI reconnects with backoff, and the tunnel’s public URL works again once it’s back. This doesn’t count as a “stop”.

Every account tunnel’s Overview tab shows a live traffic timeline: request counts, 4xx/5xx error rates, blocked-request counts, bytes in/out, and p50/p95/p99 latency. The Requests tab lists individual request events (when captureMode is headers or body), and you can replay a captured request from the console.

captureModeWhat’s recorded
none (guest default)Only aggregate stats, no per-request detail.
headersPer-request metadata + headers, but not the body.
bodyFull per-request capture including body (subject to the maxBodyBytes cap).
Requests tab with a row expanded. With captureMode: body you get the request and response bodies next to the headers. The bodies are what make the Replay button beside them useful.

MaxoPerf shows an anti-phishing interstitial screen on unverified guest tunnels. It prevents abuse and keeps automated malicious activity off public tunnel hostnames:

Tunnel tierBrowser requests (Accept: text/html)API / CLI / Webhook requests
Guest tunnel (tgs_...)Displays warning interstitial with a Proceed to Developer Tunnel buttonTransparent pass-through with non-browser User-Agent or skip header
Account tunnel (acn_...)Zero interstitial (direct pass-through)Direct pass-through

If you are calling a guest tunnel from test automation, curl, or webhook callbacks:

  • Skip header: Send X-MaxoPerf-Skip-Browser-Warning: 1 (or ngrok-skip-browser-warning: 1 for drop-in compatibility) in your HTTP request headers.
  • User-Agent: Standard non-browser User-Agent strings (such as curl/*, Postman, test runners, and webhook services) bypass the warning automatically.
  • Interactive browser cookie: Clicking the Proceed button sets a session cookie __mptt_skip_warning=1, so later requests in that browser session load directly.
  • Zero-interstitial account tunnels: Create or claim the tunnel into an authenticated MaxoPerf account to remove the interstitial for all visitors.

If you signed up after starting in guest mode, attach your running guest tunnel to your account without losing its URL:

Terminal window
curl -X POST https://app.maxoperf.com/v1/tunnels/<id>/claim \
-H "Authorization: Bearer mpak_example_key" \
-H "X-Account-Id: acn-1234567890" \
-H "Content-Type: application/json" \
-d '{ "guestToken": "mptt_g_example_guest_token" }'

The tunnel keeps its URL and moves from the guest cap to your plan’s tunnel limit. Account-tunnel configuration (access controls, capture, idle-timeout tuning) is available right away.