portless by vercel-labs/portless
npx skills add https://github.com/vercel-labs/portless --skill portless用稳定、命名的 .localhost URL 替换端口号。为人类和智能体服务。
EADDRINUSElocalhost 上的 cookie 在应用间泄漏;端口变化时 localStorage 丢失.env 文件会失效localhost:3000 的历史记录混杂了不相关的项目portless 是一个全局 CLI 工具。不要将其添加为项目依赖(不要在项目中运行 npm install portless 或 )。使用 或 。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
pnpm add portlessnpxpnpm dlx全局安装:
npm install -g portless
# 全局安装
npm install -g portless
# 启动代理(一次,无需 sudo)
portless proxy start
# 运行你的应用(如果需要会自动启动代理)
portless run next dev
# -> http://<project>.localhost:1355
# 或者使用显式名称
portless myapp next dev
# -> http://myapp.localhost:1355
当你运行应用时,代理会自动启动。你也可以使用 portless proxy start 显式启动它。
{
"scripts": {
"dev": "portless run next dev"
}
}
当你运行应用时,代理会自动启动。或者显式启动:portless proxy start。
portless myapp next dev # http://myapp.localhost:1355
portless api.myapp pnpm start # http://api.myapp.localhost:1355
portless docs.myapp next dev # http://docs.myapp.localhost:1355
默认情况下,只有显式注册的子域名会被路由(严格模式)。使用 --wildcard 启动代理以允许任何已注册路由的子域名回退到该应用(例如,tenant1.myapp.localhost:1355 无需额外注册即可路由到 myapp 应用)。精确匹配始终优先于通配符。
portless run 会自动检测 git worktrees。在链接的 worktree 中,分支名称会作为子域名前缀添加,这样每个 worktree 都会获得唯一的 URL:
# 主 worktree —— 无前缀
portless run next dev # -> http://myapp.localhost:1355
# 分支 "fix-ui" 上的链接 worktree
portless run next dev # -> http://fix-ui.myapp.localhost:1355
无需更改配置。在 package.json 中放置一次 portless run,它就在所有 worktrees 中生效。
设置 PORTLESS=0 以直接运行命令而不通过代理:
PORTLESS=0 pnpm dev # 绕过代理,使用默认端口
portless proxy start 在端口 1355 上启动一个 HTTP 反向代理作为后台守护进程(可通过 -p / --port 或 PORTLESS_PORT 环境变量配置)。当你运行应用时,代理也会自动启动。portless <name> <cmd> 通过 PORT 环境变量分配一个随机的空闲端口(4000-4999),并向代理注册该应用http://<name>.localhost:1355;代理将请求转发到应用分配的端口.localhost 域名在 Chrome、Firefox 和 Edge 中会原生解析到 127.0.0.1。Safari 依赖于系统 DNS 解析器,在某些配置下可能无法处理 .localhost 子域名。如果需要,运行 sudo portless hosts sync 将条目添加到 /etc/hosts。
大多数框架(Next.js、Express、Nuxt 等)会自动遵循 PORT 环境变量。对于忽略 PORT 的框架(Vite、Astro、React Router、Angular、Expo、React Native),portless 会自动注入正确的 --port 和 --host CLI 标志。
Portless 将其状态(路由、PID 文件、端口文件)存储在一个取决于代理端口的目录中:
/tmp/portless(仅限 macOS/Linux)~/.portless~/.portless(无特权端口概念)可通过 PORTLESS_STATE_DIR 环境变量覆盖。
| 变量 | 描述 |
|---|---|
PORTLESS_PORT | 覆盖默认代理端口(默认:1355) |
PORTLESS_APP_PORT | 为应用使用固定端口(跳过自动分配) |
PORTLESS_HTTPS | 设置为 1 以始终启用 HTTPS/HTTP/2 |
PORTLESS_TLD | 使用自定义 TLD 替代 localhost(例如 test) |
PORTLESS_WILDCARD | 设置为 1 以允许未注册的子域名回退到父级 |
PORTLESS_SYNC_HOSTS | 设置为 1 以自动同步 /etc/hosts(自定义 TLD 时自动启用) |
PORTLESS_STATE_DIR | 覆盖状态目录 |
PORTLESS=0 | 绕过代理,直接运行命令 |
使用 --https 启用 HTTP/2 多路复用(对于包含许多文件的开发服务器,页面加载更快):
portless proxy start --https # 自动生成证书并信任 CA
portless proxy start --cert ./c.pem --key ./k.pem # 使用自定义证书
sudo portless trust # 稍后将 CA 添加到信任存储
首次运行会生成本地 CA 并提示输入 sudo 以将其添加到系统信任存储。之后,不再有提示和浏览器警告。在 .bashrc/.zshrc 中设置 PORTLESS_HTTPS=1 使其永久生效。
在 Linux 上,portless trust 支持 Debian/Ubuntu、Arch、Fedora/RHEL/CentOS 和 openSUSE(通过 update-ca-certificates 或 update-ca-trust)。在 Windows 上,它使用 certutil 将 CA 添加到系统信任存储。
| 命令 | 描述 |
|---|---|
portless run <cmd> [args...] | 从项目推断名称,通过代理运行(自动启动) |
portless run --name <name> <cmd> | 覆盖推断的基础名称(worktree 前缀仍然适用) |
portless <name> <cmd> [args...] | 在 http://<name>.localhost:1355 运行应用(自动启动代理) |
portless get <name> | 打印服务的 URL(用于跨服务连接) |
portless get <name> --no-worktree | 打印不带 worktree 前缀的 URL |
portless list | 显示活动路由 |
portless trust | 将本地 CA 添加到系统信任存储(用于 HTTPS) |
portless proxy start | 以守护进程方式启动代理(端口 1355,无需 sudo) |
portless proxy start --https | 使用 HTTP/2 + TLS 启动(自动生成证书) |
portless proxy start -p <number> | 在自定义端口启动代理 |
portless proxy start --tld test | 使用 .test 替代 .localhost(需要同步 /etc/hosts) |
portless proxy start --foreground | 在前台启动代理(用于调试) |
portless proxy start --wildcard | 允许未注册的子域名回退到父级路由 |
portless proxy stop | 停止代理 |
portless alias <name> <port> | 注册静态路由(例如用于 Docker 容器) |
portless alias <name> <port> --force | 覆盖现有路由 |
portless alias --remove <name> | 移除静态路由 |
portless hosts sync | 将路由添加到 /etc/hosts(修复 Safari) |
portless hosts clean | 从 /etc/hosts 中移除 portless 条目 |
portless <name> --app-port <n> <cmd> | 为应用使用固定端口而非自动分配 |
portless <name> --force <cmd> | 覆盖由另一个进程注册的现有路由 |
portless --name <name> <cmd> | 强制使用 <name> 作为应用名称(绕过子命令分发) |
portless <name> -- <cmd> [args...] | 停止标志解析;-- 之后的所有内容都传递给子进程 |
portless --help / -h | 显示帮助 |
portless run --help | 显示子命令的帮助(同样适用于:alias, hosts) |
portless --version / -v | 显示版本 |
保留名称: run、get、alias、hosts、list、trust 和 proxy 是子命令,不能直接用作应用名称。使用 portless run <cmd> 来推断名称,或使用 portless --name <name> <cmd> 来强制使用任何名称(包括保留名称)。
当你使用 portless <name> <cmd> 运行应用时,代理会自动启动。如果它没有启动(例如端口冲突),请手动启动:
portless proxy start
另一个进程绑定了代理端口。要么先停止它,要么使用不同的端口:
portless proxy start -p 8080
Portless 会自动为忽略 PORT 环境变量的框架注入 --port 和 --host 标志:Vite、Astro、React Router、Angular、Expo 和 React Native。SvelteKit 内部使用 Vite,会自动处理。
对于其他不读取 PORT 的框架,手动传递端口:
--port $PORTprocess.env.PORT 并监听它低于 1024 的端口需要 sudo。默认端口(1355)不需要 sudo。如果你想使用端口 80:
sudo portless proxy start -p 80 # 端口 80,需要 sudo
portless proxy start # 端口 1355,无需 sudo
portless proxy stop # 停止(如果使用 sudo 启动,则使用 sudo 停止)
Safari 依赖于系统 DNS 解析器来处理 .localhost 子域名,在某些 macOS 配置下可能无法解析它们。Chrome、Firefox 和 Edge 有内置处理。
修复:
sudo portless hosts sync # 将当前路由添加到 /etc/hosts
sudo portless hosts clean # 稍后移除条目
对于自定义 TLD(例如 --tld test),会自动同步 /etc/hosts。对于 .localhost,设置 PORTLESS_SYNC_HOSTS=1 来启用。
本地 CA 可能尚未被信任。运行:
sudo portless trust
这会将 portless 本地 CA 添加到你的系统信任存储。之后,重启浏览器。
如果你的开发服务器将请求代理到另一个 portless 应用(例如 Vite 将 /api 代理到 api.myapp.localhost:1355),代理必须重写 Host 标头。如果没有这个,portless 会将请求路由回原始应用,造成无限循环。
修复:在代理配置(Vite、webpack-dev-server 等)中设置 changeOrigin: true:
// vite.config.ts
proxy: {
"/api": {
target: "http://api.myapp.localhost:1355",
changeOrigin: true,
ws: true,
},
}
openssl(用于 --https 证书生成;macOS 和大多数 Linux 发行版自带)每周安装量
412
代码仓库
GitHub 星标
6.3K
首次出现
2026年2月15日
安全审计
安装于
codex399
opencode396
gemini-cli393
github-copilot393
amp388
kimi-cli387
Replace port numbers with stable, named .localhost URLs. For humans and agents.
EADDRINUSE when two projects default to the same portlocalhost bleed across apps; localStorage lost when ports shift.env files break when ports changelocalhost:3000 history is a mix of unrelated projectsportless is a global CLI tool. Do NOT add it as a project dependency (no npm install portless or pnpm add portless in a project). Do NOT use npx or pnpm dlx.
Install globally:
npm install -g portless
# Install globally
npm install -g portless
# Start the proxy (once, no sudo needed)
portless proxy start
# Run your app (auto-starts the proxy if needed)
portless run next dev
# -> http://<project>.localhost:1355
# Or with an explicit name
portless myapp next dev
# -> http://myapp.localhost:1355
The proxy auto-starts when you run an app. You can also start it explicitly with portless proxy start.
{
"scripts": {
"dev": "portless run next dev"
}
}
The proxy auto-starts when you run an app. Or start it explicitly: portless proxy start.
portless myapp next dev # http://myapp.localhost:1355
portless api.myapp pnpm start # http://api.myapp.localhost:1355
portless docs.myapp next dev # http://docs.myapp.localhost:1355
By default, only explicitly registered subdomains are routed (strict mode). Start the proxy with --wildcard to allow any subdomain of a registered route to fall back to that app (e.g. tenant1.myapp.localhost:1355 routes to the myapp app without extra registration). Exact matches always take priority over wildcards.
portless run automatically detects git worktrees. In a linked worktree, the branch name is prepended as a subdomain prefix so each worktree gets a unique URL:
# Main worktree -- no prefix
portless run next dev # -> http://myapp.localhost:1355
# Linked worktree on branch "fix-ui"
portless run next dev # -> http://fix-ui.myapp.localhost:1355
No config changes needed. Put portless run in package.json once and it works in all worktrees.
Set PORTLESS=0 to run the command directly without the proxy:
PORTLESS=0 pnpm dev # Bypasses proxy, uses default port
portless proxy start starts an HTTP reverse proxy on port 1355 as a background daemon (configurable with -p / --port or the PORTLESS_PORT env var). The proxy also auto-starts when you run an app.portless <name> <cmd> assigns a random free port (4000-4999) via the PORT env var and registers the app with the proxyhttp://<name>.localhost:1355 on the proxy port; the proxy forwards to the app's assigned port.localhost domains resolve to 127.0.0.1 natively in Chrome, Firefox, and Edge. Safari relies on the system DNS resolver, which may not handle .localhost subdomains on all configurations. Run sudo portless hosts sync to add entries to /etc/hosts if needed.
Most frameworks (Next.js, Express, Nuxt, etc.) respect the PORT env var automatically. For frameworks that ignore PORT (Vite, Astro, React Router, Angular, Expo, React Native), portless auto-injects the correct --port and --host CLI flags.
Portless stores its state (routes, PID file, port file) in a directory that depends on the proxy port:
/tmp/portless (macOS/Linux only)~/.portless~/.portless (no privileged port concept)Override with the PORTLESS_STATE_DIR environment variable.
| Variable | Description |
|---|---|
PORTLESS_PORT | Override the default proxy port (default: 1355) |
PORTLESS_APP_PORT | Use a fixed port for the app (skip auto-assignment) |
PORTLESS_HTTPS | Set to 1 to always enable HTTPS/HTTP/2 |
PORTLESS_TLD | Use a custom TLD instead of localhost (e.g. test) |
PORTLESS_WILDCARD | Set to 1 to allow unregistered subdomains to fall back to parent |
Use --https for HTTP/2 multiplexing (faster page loads for dev servers with many files):
portless proxy start --https # Auto-generate certs and trust CA
portless proxy start --cert ./c.pem --key ./k.pem # Use custom certs
sudo portless trust # Add CA to trust store later
First run generates a local CA and prompts for sudo to add it to the system trust store. After that, no prompts and no browser warnings. Set PORTLESS_HTTPS=1 in .bashrc/.zshrc to make it permanent.
On Linux, portless trust supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via update-ca-certificates or update-ca-trust). On Windows, it uses certutil to add the CA to the system trust store.
| Command | Description |
|---|---|
portless run <cmd> [args...] | Infer name from project, run through proxy (auto-starts) |
portless run --name <name> <cmd> | Override inferred base name (worktree prefix still applies) |
portless <name> <cmd> [args...] | Run app at http://<name>.localhost:1355 (auto-starts proxy) |
portless get <name> | Print URL for a service (for cross-service wiring) |
portless get <name> --no-worktree |
Reserved names: run, get, alias, hosts, list, trust, and proxy are subcommands and cannot be used as app names directly. Use portless run <cmd> to infer the name, or portless --name <name> <cmd> to force any name including reserved ones.
The proxy auto-starts when you run an app with portless <name> <cmd>. If it doesn't start (e.g. port conflict), start it manually:
portless proxy start
Another process is bound to the proxy port. Either stop it first, or use a different port:
portless proxy start -p 8080
Portless auto-injects --port and --host flags for frameworks that ignore the PORT env var: Vite , Astro , React Router , Angular , Expo , and React Native. SvelteKit uses Vite internally and is handled automatically.
For other frameworks that don't read PORT, pass the port manually:
--port $PORTprocess.env.PORT and listen on itPorts below 1024 require sudo. The default port (1355) does not need sudo. If you want to use port 80:
sudo portless proxy start -p 80 # Port 80, requires sudo
portless proxy start # Port 1355, no sudo needed
portless proxy stop # Stop (use sudo if started with sudo)
Safari relies on the system DNS resolver for .localhost subdomains, which may not resolve them on all macOS configurations. Chrome, Firefox, and Edge have built-in handling.
Fix:
sudo portless hosts sync # Adds current routes to /etc/hosts
sudo portless hosts clean # Remove entries later
Auto-syncs /etc/hosts for custom TLDs (e.g. --tld test). For .localhost, set PORTLESS_SYNC_HOSTS=1 to enable.
The local CA may not be trusted yet. Run:
sudo portless trust
This adds the portless local CA to your system trust store. After that, restart the browser.
If your dev server proxies requests to another portless app (e.g. Vite proxying /api to api.myapp.localhost:1355), the proxy must rewrite the Host header. Without this, portless routes the request back to the original app, creating an infinite loop.
Fix: set changeOrigin: true in the proxy config (Vite, webpack-dev-server, etc.):
// vite.config.ts
proxy: {
"/api": {
target: "http://api.myapp.localhost:1355",
changeOrigin: true,
ws: true,
},
}
openssl (for --https cert generation; ships with macOS and most Linux distributions)Weekly Installs
412
Repository
GitHub Stars
6.3K
First Seen
Feb 15, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
codex399
opencode396
gemini-cli393
github-copilot393
amp388
kimi-cli387
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
PORTLESS_SYNC_HOSTS | Set to 1 to auto-sync /etc/hosts (auto-enabled for custom TLDs) |
PORTLESS_STATE_DIR | Override the state directory |
PORTLESS=0 | Bypass the proxy, run the command directly |
| Print URL without worktree prefix |
portless list | Show active routes |
portless trust | Add local CA to system trust store (for HTTPS) |
portless proxy start | Start the proxy as a daemon (port 1355, no sudo) |
portless proxy start --https | Start with HTTP/2 + TLS (auto-generates certs) |
portless proxy start -p <number> | Start the proxy on a custom port |
portless proxy start --tld test | Use .test instead of .localhost (requires /etc/hosts sync) |
portless proxy start --foreground | Start the proxy in foreground (for debugging) |
portless proxy start --wildcard | Allow unregistered subdomains to fall back to parent route |
portless proxy stop | Stop the proxy |
portless alias <name> <port> | Register a static route (e.g. for Docker containers) |
portless alias <name> <port> --force | Overwrite an existing route |
portless alias --remove <name> | Remove a static route |
portless hosts sync | Add routes to /etc/hosts (fixes Safari) |
portless hosts clean | Remove portless entries from /etc/hosts |
portless <name> --app-port <n> <cmd> | Use a fixed port for the app instead of auto-assignment |
portless <name> --force <cmd> | Override an existing route registered by another process |
portless --name <name> <cmd> | Force <name> as app name (bypasses subcommand dispatch) |
portless <name> -- <cmd> [args...] | Stop flag parsing; everything after -- is passed to child |
portless --help / -h | Show help |
portless run --help | Show help for a subcommand (also: alias, hosts) |
portless --version / -v | Show version |