---
title: "Changelog"
description: "Replace port numbers with stable, named .localhost URLs. For humans and agents."
canonical_url: "https://portless.sh/changelog"
---
# Changelog

## 0.15.6

### New Features

- **Agent-readable documentation**: The documentation site now exposes clean Markdown pages and an `llms.txt` index so agents can discover and consume the docs directly

### Bug Fixes

- **Framework flags through package scripts**: Frameworks that ignore `PORT` now receive the assigned `--port` and `--host` flags when launched through supported package-manager scripts, while non-server and unsafe scripts are left untouched
- **Windows service lifetime**: The Windows startup service now registers a Task Scheduler definition with no execution time limit and replaces existing tasks atomically
- **Ctrl+C process cleanup**: Interrupt handling now waits for the command's process tree to exit and terminates remaining descendants after a short grace period

## 0.15.5

### Bug Fixes

- **Multi-segment custom TLDs**: `--tld` and `PORTLESS_TLD` now accept dotted DNS names such as `dev.example.com`, so local URLs can mirror production structure. Validation checks per-label DNS rules and the 253-character total limit, and overlapping TLDs resolve by longest match
- **WebSocket over HTTP/2**: Proxy now advertises RFC 8441 extended CONNECT and bridges WebSocket streams to an HTTP/1.1 upgrade against the backend, fixing dev server HMR for browsers that negotiate h2 over ALPN. Plain-HTTP upgrades on the TLS port are proxied instead of dropped

## 0.15.4

### Bug Fixes

- **Loopback-only proxy binding**: Outside LAN mode, the proxy and HTTP redirect listeners now bind only to `127.0.0.1` and `::1`, so Portless routes cannot be reached through LAN, VPN, or other network interfaces. LAN mode still binds to all interfaces explicitly

## 0.15.3

### Bug Fixes

- **State directory under sudo**: Portless now resolves per-user state from the original sudo user's home, so an elevated proxy and unprivileged app processes share the same routes instead of writing to separate state directories
- **Windows and WSL CA trust**: On WSL, `portless trust` now installs the local CA in both Linux and Windows trust stores, while `portless clean` removes the exact certificate from both. Failed trust-store cleanup preserves the CA identity for safe retries, including on native Windows

## 0.15.2

### Bug Fixes

- **Tailscale funnel routing**: Proxy now routes requests addressed to a route's Tailscale funnel or serve hostname, so `--funnel` and `--tailscale` apps reached at `<device>.ts.net` no longer return a 404, including when several apps share one hostname on different ports
- **IPv6-only dev servers return 502**: Proxy now dials upstreams over both loopback families, fixing 502s when a dev server binds `::1` only, such as Vite on Node 17+
- **Worktree prefix in multi-app mode**: Bare `portless` in a monorepo worktree now applies the branch prefix in multi-app mode as it already did for single apps, so hostnames no longer collide across worktrees

## 0.15.1

### New Features

- **Multi-TLD proxy support**: `--tld` is now repeatable and `PORTLESS_TLD` accepts comma separated values, so one proxy can serve the same app names across multiple TLDs. Routes, TLS, service state, hosts sync, framework environment, and workspace launches now use the full configured TLD list

## 0.15.0

### New Features

- **`portless doctor`**: New read only diagnostics command checks Node.js, the state directory, proxy liveness, route entries, hostname resolution, HTTPS CA trust, and LAN prerequisites, then prints suggested fixes

### Bug Fixes

- **HTTP/2 Host forwarding**: Proxy now forwards HTTP/2 `:authority` as `Host` to HTTP/1.1 backends, fixing apps that depend on Host and previously saw `127.0.0.1` for browser traffic
- **`--force` takeover cleanup**: Exit cleanup now removes only routes still owned by the exiting process, so a forced takeover does not deregister the new owner's route

## 0.14.0

### New Features

- **ngrok sharing**: New `--ngrok` flag exposes portless apps publicly through ngrok while keeping the local `.localhost` URL. Also configurable with `PORTLESS_NGROK=1`; child processes receive `PORTLESS_NGROK_URL`, and `portless list` shows active ngrok URLs

### Improvements

- **ngrok tunnel lifecycle**: Portless checks for the ngrok CLI before starting an app, surfaces install and authentication guidance, removes stopped ngrok URLs from route state, and terminates tunnel processes during app cleanup

## 0.13.1

### New Features

- **Configurable startup services**: `portless service install` now persists proxy options such as `--port`, `--no-tls`, `--lan`, `--ip`, `--tld`, `--wildcard`, `--cert`, `--key`, and `--state-dir` into launchd, systemd, and Task Scheduler. `portless service status` now reports the installed service port, HTTPS mode, TLD, LAN mode, wildcard mode, and state directory

### Bug Fixes

- **Service reinstall port changes**: Reinstalling the startup service with a different port now stops any existing proxy on the previous port before starting the new service
- **Service install validation**: LAN service installs now fail early on platforms that cannot publish mDNS records, and service paths such as `~` are normalized before writing native service files

### Requirements

- **Node.js 24**: The published package now requires Node.js 24 or newer, and repository development uses pnpm 11 with a minimum release age policy

## 0.13.0

### New Features

- **OS startup service**: New `portless service install`, `portless service status`, and `portless service uninstall` commands manage a native startup service for the HTTPS proxy across macOS launchd, Linux systemd, and Windows Task Scheduler. The service starts clean `.localhost` URLs after reboot, and `portless clean` removes it during cleanup

### Bug Fixes

- **Tailscale readiness preflight**: `--tailscale` and `--funnel` now check Tailscale HTTPS and Funnel prerequisites before starting the child process, surfacing actionable errors instead of hanging during registration

## 0.12.0

### New Features

- **Tailscale sharing**: New `--tailscale` flag shares any portless app over your Tailscale network with zero framework config. Each app is root-mounted on its own Tailscale HTTPS port (443, 8443, 8444, ...) so no `basePath` configuration is needed. Works as a global flag, per-app flag (`portless myapp --tailscale next dev`), or env var (`PORTLESS_TAILSCALE=1`)
- **Tailscale Funnel**: New `--funnel` flag exposes apps to the public internet through Tailscale Funnel. Implies `--tailscale`. Also configurable via `PORTLESS_FUNNEL=1`
- **`PORTLESS_TAILSCALE_URL` env var**: Child processes receive `PORTLESS_TAILSCALE_URL` containing the Tailscale HTTPS URL so apps can reference their public address
- **Tailscale URLs in `portless list`**: The list command now shows tailnet URLs alongside local URLs when Tailscale sharing is active

### Improvements

- **`portless prune` cleans stale Tailscale registrations**: Prune now removes orphaned `tailscale serve` entries left behind by dead CLI sessions
- **`portless clean` removes Tailscale serve state**: Clean now tears down any Tailscale serve/funnel registrations alongside the usual CA and hosts file cleanup

## 0.11.1

### New Features

- **`portless prune` command**: Safety net to find and kill orphaned dev servers left behind by dead CLI sessions. Reads stale route entries, checks if something is still listening on each port, and terminates the orphan.

### Bug Fixes

- **Zombie process orphaning on CLI crash**: Spawn child processes with `detached:true` on Unix so they get their own process group. Signal handlers now kill the entire group instead of just the immediate child, preventing orphaned dev servers from surviving CLI crashes or `kill -9`.

## 0.11.0

### New Features

- **Zero-arg mode**: Run bare `portless` from any project directory to auto-discover the dev script from `package.json` and start it through the proxy. No arguments, no config required
- **Multi-app orchestration**: In monorepos, bare `portless` auto-discovers workspace packages (pnpm, npm, yarn, bun) and starts all dev scripts concurrently through the proxy. Each package gets a subdomain derived from its npm scope (e.g. `@acme/docs` becomes `docs.acme.localhost`)
- **Turborepo integration**: When `turbo.json` is present, portless delegates to `turbo run <script>` instead of spawning each app individually. Per-app `PORT`, `HOST`, and `PORTLESS_URL` are injected via a lightweight `--require` loader so turbo retains dependency ordering and task graph awareness. Set `turbo: false` in config to opt out
- **`portless.json` config file**: Configure app name, script, port, and turbo settings without embedding portless in `package.json` scripts. Also supports a `"portless"` key in `package.json` as an inline alternative
- **`--script` flag**: Override the default `"dev"` script for a single invocation (e.g. `portless --script start`)
- **Rsbuild support**: Auto-inject `--port` and `--host` CLI flags for Rsbuild dev server

### Bug Fixes

- **State directory moved to `~/.portless`**: All proxy state now lives in `~/.portless` instead of `/tmp/portless`, fixing repeated CA trust prompts on macOS (where `/tmp` is periodically cleaned) and a symlink local privilege escalation vulnerability
- **Duplicate macOS CA certificates**: Fix `security delete-certificate` failing with "is ambiguous" when multiple portless CA entries had accumulated in the keychain
- **CA trust marker caching**: Cache the CA fingerprint after a successful trust so subsequent proxy starts skip the OS security check and avoid re-triggering the macOS authentication dialog

### Improvements

- **Auto-trust CA on proxy auto-start**: When the proxy is auto-started and the CA is not yet trusted, portless automatically runs trust with proper sudo elevation
- **Package manager delegation**: Detects pnpm, yarn, bun, or npm and delegates script execution to the correct package manager
- **Non-server script detection**: Build-only tools (tsup, tsc, esbuild, etc.) are auto-detected and run without a proxy route. Use `proxy: false` in config for explicit control
- **Monochrome CLI output**: Bold for headers and errors, dim for warnings and muted text, no color codes

## 0.10.3

### Bug Fixes

- **Stale TLD persisting across proxy restart**: Fix the proxy reverting to a stale TLD (e.g. `.local`) after restart because transient state markers were not cleaned up on stop

## 0.10.2

### New Features

- **Auto-inject `NODE_EXTRA_CA_CERTS`**: Child processes spawned by `portless run` now automatically receive `NODE_EXTRA_CA_CERTS` pointing to the portless CA certificate, so Node.js subprocesses trust the local CA without manual configuration

### Bug Fixes

- **Proxy startup on slow macOS `security` command**: Fix the proxy failing to start when the macOS `security` command takes longer than expected to verify CA trust
- **Lock contention with parallel commands**: Fix lock contention that could cause failures when multiple `portless` commands run simultaneously
- **`ERR_HTTP2_PROTOCOL_ERROR` during HMR**: Fix HTTP/2 stream reset flood during hot module replacement causing protocol errors
- **Proxy auto-start in non-interactive terminals**: Fix auto-start failing in non-interactive terminals (e.g. IDE task runners) and when previous proxy config exists

## 0.10.1

### New Features

- **`portless clean`**: New command stops the proxy if it is running, removes the local CA from the OS trust store when it was installed by portless, deletes allowlisted files under known state directories, and removes the portless-managed block from the hosts file. Custom `--cert` and `--key` paths are never removed

### Improvements

- **Hosts file sync by default**: The proxy now keeps the hosts file in sync with active routes automatically (improves Safari and other setups where `.localhost` subdomains do not resolve to loopback). Set `PORTLESS_SYNC_HOSTS=0` to opt out. The managed block is removed from the hosts file when the proxy exits

## 0.10.0

### New Features

- **LAN mode**: New `--lan` flag exposes portless services to phones and other devices on the same network via mDNS `.local` hostnames. Auto-detects the active LAN IP, follows network changes, and supports `--ip` / `PORTLESS_LAN_IP` overrides for VPN or multi-interface setups. Publishes mDNS records with platform-native tools (`dns-sd` on macOS, `avahi-publish-address` on Linux). Adds `*.local` to generated certificate SANs so HTTPS works for LAN hostnames
- **VitePlus support**: Auto-inject `--port` for VitePlus (`vp`) dev server

## 0.9.6

### Bug Fixes

- **WebSocket proxy memory leak**: Add socket close/end handlers to prevent memory leaks in the WebSocket proxy

## 0.9.5

### Bug Fixes

- **`--force` kills existing process**: `--force` now terminates the process that owns the conflicting route before registering a new one, instead of only removing the stale route entry
- **CA certificate included in TLS chain**: The proxy now sends the CA certificate as part of the TLS chain, fixing `UNABLE_TO_VERIFY_LEAF_SIGNATURE` errors in clients that do not have the portless CA in their trust store

## 0.9.4

### Bug Fixes

- **README missing from npm package**: The published npm package now includes its README. Previously `.gitignore` excluded the copied README during packing; an `.npmignore` override fixes this

## 0.9.3

### Breaking Changes

- **Origin/Referer header rewriting removed**: The proxy no longer rewrites `Origin` and `Referer` headers. The feature introduced in 0.9.2 caused issues with certain backend frameworks and has been removed

## 0.9.2

### New Features

- **Origin/Referer header rewriting**: The proxy now rewrites `Origin` and `Referer` headers for portless-managed hostnames so backend CSRF protections accept proxied requests

### Bug Fixes

- **Browser-blocked ports excluded from auto-selection**: Ports that browsers refuse to connect to (e.g. 6666, 6667) are now excluded from automatic port assignment
- **State directory preserved during sudo elevation**: Fix `portless trust` losing the state directory when elevating to sudo
- **Windows OpenSSL config detection**: Auto-detect `openssl.cnf` location on Windows when `OPENSSLDIR` points to a non-existent path

## 0.9.1

### New Features

- **Project dev dependency install**: portless can now be installed as a project dev dependency (`npm install -D portless`) in addition to the global install. The `npx`/`dlx` guard now only blocks one-off downloads, not locally installed packages

### Bug Fixes

- **`portless trust` on fresh install**: Fix `portless trust` failing on a fresh install when no CA certificate exists yet. The command now generates the CA and server certificates automatically before trusting

## 0.9.0

### Breaking Changes

- **HTTPS on port 443 is now the default**: The proxy defaults to HTTPS on port 443 instead of HTTP on port 1355. Auto-elevates with sudo on macOS/Linux to bind privileged ports. Use `--no-tls` for plain HTTP on port 80, or `-p 1355` for the previous unprivileged port
- **`PORTLESS_HTTPS` env var inverted**: HTTPS is on by default; set `PORTLESS_HTTPS=0` to disable (replaces the old `PORTLESS_HTTPS=1` opt-in)

### New Features

- **HTTP-to-HTTPS redirect**: When the HTTPS proxy runs on port 443, a companion HTTP server on port 80 automatically redirects all requests to HTTPS
- **Auto-sudo for proxy lifecycle**: `portless proxy start` auto-elevates with sudo when binding privileged ports. `portless proxy stop` does the same when the running proxy is owned by root
- **Clean URLs**: URLs are now `https://myapp.localhost` instead of `http://myapp.localhost:1355`. No port numbers to remember

## 0.8.0

### Breaking Changes

- **Strict subdomain routing is now the default**: Subdomains no longer automatically match parent hostnames (e.g. `api.myapp.localhost` no longer routes to `myapp.localhost`). Use the `--wildcard` flag or `PORTLESS_WILDCARD=1` env var to restore the previous behavior

### New Features

- **`--wildcard` flag**: Opt in to wildcard subdomain routing where subdomains match registered parent hostnames. Configurable via `PORTLESS_WILDCARD` env var

### Bug Fixes

- **Cert generation with dots in `$HOME`**: Fix TLS certificate generation failing when the home directory path contains dots
- **DNS label limit for `--name` flag**: Fix regression where long `--name` values could exceed the 63-character DNS label limit
- **Windows `DEP0190` deprecation warning**: Silence Node.js deprecation warning on Windows by replacing `shell: true` with explicit `cmd.exe /d /s /c` spawning
- **Windows duplicate `PATH` entries**: Deduplicate `PATH` environment variables in child process spawn on Windows

### Improvements

- **Removed chalk dependency**: Replaced chalk with lightweight ANSI color utilities to reduce install size
- **Automated release process**: Added CI workflow for automated npm publishing and GitHub releases

## 0.7.2

### Bug Fixes

- **`--port` injection for package runners**: Fix `--port` injection for commands run via package runners like `npx`, `pnpm dlx`, etc. (#150)
- **TLS cert generation**: Fix TLS cert generation for long hostnames and proxy startup races (#149)
- **Proxy crash on ECONNRESET**: Handle `ECONNRESET` errors on TLS wrapper sockets to prevent proxy crash (#127)
- **Windows `node not recognized`**: Resolve `node not recognized` error on Windows when running `portless run` (#126)

### Documentation

- Added Windows to docs requirements section (#122)

### Improvements

- Added GitHub Action to automatically publish packages to npm on release (#130)

## 0.7.1

### Documentation

- Updated docs site header (#118)

## 0.7.0

### Features

- **Windows support**: Full cross-platform support for Windows. Uses `os.tmpdir()` for state directory, `netstat` for port detection, `certutil` for CA trust, and platform-aware command spawning. Includes Windows CI

### Bug Fixes

- **`--name` sanitization in `portless run`**: Stop replacing dots with hyphens in `--name` values. Dots are valid and intentional in hostnames like `local.metaview`
- **Worktree prefix only for linked worktrees**: Only prepend the branch name for linked worktrees, not the root worktree. Previously any non-main branch got a prefix when multiple worktrees existed
- **Windows hosts file paths**: Use platform-aware hosts file path and error messages (Administrator vs sudo)

## 0.6.0

### Features

- **Custom TLD**: Use `--tld` to set a custom TLD (e.g. `.test`) instead of `.localhost`. Configurable via `PORTLESS_TLD` env var. Auto-syncs `/etc/hosts` for custom TLDs when started with sudo. Warns about risky TLDs like `.local` and `.dev`. Recommended: `.test` (IANA-reserved, no collision risk)
- **`portless get` command**: Print the URL for a service, useful for wiring services together (e.g. `BACKEND_URL=$(portless get backend)`). Applies worktree prefix by default; use `--no-worktree` to skip
- **`--name` flag for `portless run`**: Override the inferred base name while preserving the worktree prefix (e.g. `portless run --name myapp next dev` in a worktree produces `fix-ui.myapp.localhost`)

### Bug Fixes

- **HTTPS proxy trust and stop on macOS with sudo**: Fix CA trust check and `proxy stop` when the proxy was started with sudo on macOS
- **DNS label length for worktree hostnames**: Truncate worktree-prefixed hostnames to respect the 63-character DNS label limit

## 0.5.2

### Documentation

- Add git worktree documentation. `portless run` automatically detects linked worktrees and prefixes the URL with the branch name (e.g. `fix-ui.myapp.localhost`)

## 0.5.1

### Bug Fixes

- **npm README**: Copy root `README.md` into the package at publish time so it appears on npmjs.com
- **homepage**: Point npm homepage to https://port1355.dev

## 0.5.0

### Features

- **`portless run` subcommand**: Automatically infer the project name from `package.json`, git root, or directory name instead of specifying it manually. In git worktrees, the branch name is prepended as a subdomain prefix (e.g. `fix-ui.myapp.localhost`) so each worktree gets a unique URL with no config changes
- **`portless alias` command**: Register routes for services not spawned by portless (e.g. Docker containers with published ports)
- **`PORTLESS_URL` env var**: Child processes receive `PORTLESS_URL` containing the public `.localhost` URL so apps can self-reference their own URL
- **`--app-port` flag**: Specify a fixed port for the app instead of automatic assignment, also configurable via `PORTLESS_APP_PORT` env var
- **Wildcard subdomain routing**: Subdomains now match registered hostnames (e.g. `tenant.myapp.localhost` routes to `myapp.localhost`). Exact matches take priority
- **`/etc/hosts` sync**: Automatically sync `.localhost` hostnames to `/etc/hosts` for environments where `.localhost` does not resolve to `127.0.0.1`
- **Multi-distro Linux CA trust**: `portless trust` now supports Arch, Fedora/RHEL/CentOS, and openSUSE in addition to Debian/Ubuntu
- **Expo and React Native support**: Auto-inject `--port` and `--host` flags for `expo start` and `react-native start`
- **Branded error and status pages**: The proxy renders styled HTML pages for 404, 502, 508, and other status codes

### Bug Fixes

- **Stream errors**: Handle proxy stream errors gracefully to prevent unhandled exceptions from crashing the proxy

## 0.4.2

### Bug Fixes

- **spawn ENOENT**: Use `/bin/sh -c` for command execution so shell scripts and version-manager shims (nvm, fnm, mise) are resolved correctly. Prepend `node_modules/.bin` to `PATH` so local project binaries (e.g. `next`, `vite`) are found without a global install.
- **sudo state directory permissions**: System state directory (`/tmp/portless`) now uses world-writable + sticky-bit permissions so non-root processes can register routes after a sudo proxy start. State files created under sudo are chowned back to the real user.
- **duplicate route names**: `addRoute` now checks for an existing live route and throws an error if the hostname is already registered by a running process. Use `--force` to override.
- **TLS SHA-1 rejection**: Force SHA-256 for all CA and server certificate generation. Detect and regenerate existing SHA-1 certificates automatically.
- **per-hostname certs for `.localhost` subdomains**: Issue a per-hostname certificate with an exact SAN for every `.localhost` subdomain. `*.localhost` wildcard certs are invalid because `.localhost` is a reserved TLD per RFC 2606.
- **terminal left in raw mode**: Reset `stdin.setRawMode(false)` on process exit so the terminal is not left in raw mode after Ctrl+C.

### Features

- **proxy loop detection**: Detect forwarding loops (e.g. a Vite dev server proxying back through portless without `changeOrigin: true`) using the `X-Portless-Hops` header. Respond with `508 Loop Detected` and a message explaining the fix. Also detects loops on WebSocket upgrades.
- **`--force` flag**: Override a route registered by another process with `portless <name> --force <cmd>`.

## 0.4.1

- Fixed Vite support by auto-injecting `--port` and `--host` flags
- Added e2e tests for 11 frameworks (Next.js, Vite, Nuxt, Astro, Angular, SvelteKit, Remix, Solid, React Router, Hono, Express)
- Improved docs site
- Added Turborepo for build orchestration

## 0.4.0

- HTTP/2 + TLS support with auto-generated certificates
- `sudo portless trust` command for system CA trust
- `--https`, `--cert`, `--key`, `--no-tls` flags for proxy start
- HTTPS configuration via `portless.json` and `PORTLESS_HTTPS` env var

## 0.3.0

- Configuration file support (`portless.json`)
- `PORTLESS=0` to bypass the proxy
- Port range customization

## 0.2.2

- Bug fixes and stability improvements

## 0.2.1

- Fixed proxy routing issues

## 0.2.0

- Added `--port` flag to the proxy command

## 0.1.0

- Initial release
- Reverse proxy on port 1355
- Named `.localhost` URLs for dev servers
- `portless list` command
- Subdomain support
