Get started
BETA
Browse docs
Concepts

Subdomains

How JustTunnel routes a public URL to a tunnel — random vs reserved names, DNS rules, and per-tier behavior.

Every tunnel gets a public URL on <name>.justtunnel.dev. The <name> part is the subdomain — JustTunnel uses it as the routing key on every inbound request. Names come in three flavors: random per session, requested ad hoc, and reserved across sessions.

How it works

When a request arrives at the JustTunnel edge, the host header (acme-staging.justtunnel.dev) is parsed to extract the subdomain (acme-staging). The edge looks up the active tunnel for that subdomain and forwards the request over its WebSocket. No active tunnel means a 502 from the edge — see Tunnel anatomy for the request path.

The DNS side is wildcard: *.justtunnel.dev resolves to the edge regardless of subdomain. There's no per-tunnel DNS record to provision and no propagation delay — the moment your CLI hands the edge a subdomain, traffic to that hostname starts working.

Three name flavors

Random per session (default)

Run justtunnel <port> with no flag and the CLI gets a friendly two-word name like wandering-otter-42. The name lives only as long as the WebSocket — close the tunnel and the name is released for someone else.

justtunnel 3000
# https://wandering-otter-42.justtunnel.dev

Random names are the right choice for one-off testing where the URL never leaves your terminal.

Requested ad hoc

Pass --subdomain <name> (or -s) to ask the edge for a specific name for this session only. If nobody else has it open right now, the edge accepts. If somebody does — including a paid user who has reserved the name — you get subdomain unavailable and the tunnel exits.

justtunnel 3000 --subdomain acme-staging

Free and Starter plans can request any unused name, but they cannot hold it across restarts. The next user who asks for acme-staging gets it. See Plans and limits for which tiers can hold names across sessions.

Reserved across sessions

Reserved subdomains are a Pro and Team plan feature. A reservation marks a name as belonging to your account so nobody else can claim it, even when no tunnel is active. The CLI invocation stays exactly the same — you reserve in the dashboard, then keep running justtunnel 3000 --subdomain acme-staging as before.

Server-side, reserved names are enforced at tunnel-create time (internal/plan/enforcer.go:103 rejects with RESERVED_SUBDOMAIN_NOT_ALLOWED for plans that lack AllowReservedSub). Walkthrough in Reserve a custom subdomain.

Naming rules

The edge validates every requested name against this regex (internal/subdomain/validation.go:8):

^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$

In English:

  • 3 to 30 characters total
  • lowercase letters, digits, and hyphens only
  • must start and end with an alphanumeric (no leading or trailing hyphen)
  • no underscores, dots, uppercase, or non-ASCII

Good: acme-staging, paul-webhooks, client-preview-2026, api-v3. Rejected: Acme-Staging (uppercase), _internal (underscore + leading hyphen), -edge (leading hyphen), ab (too short), a * 40 (too long).

Worker tunnels follow a slightly different shape — names are <worker-name>--<team-slug> and the CLI pre-validates so the derived subdomain still fits the 63-character DNS label limit. See Worker tunnels.

Limits and guarantees

What's enforced today:

  • Reservation requires Pro or Team. Free and Starter cannot reserve at all (AllowReservedSub: false). Pro reserves up to 10 names; Team reserves up to 20 per team (not per seat). From internal/plan/limits.go.
  • Reservation is per-account (or per-team). Personal-context reservations belong to your user; team-context reservations belong to the team. Switching contexts does not move reservations across.
  • Reservation does not survive plan downgrade. If billing lapses and your plan drops below Pro, the names stop being held — the next requester wins them. See Reserve a custom subdomain.

Best-effort, not guaranteed:

  • Memorability of random names. The pool is large but finite; we don't guarantee a specific random name across runs.
  • System-reserved names. Some names are blocked by the abuse layer (brand impersonation patterns in internal/abuse/patterns/patterns.go). The error you get is subdomain unavailable — pick another name.

On this page