portless 环境变量实战:从稳定本地 URL 到手机直连与公网分享的调法
【免费下载链接】portlessReplace port numbers with stable, named local URLs. For humans and agents.项目地址: https://gitcode.com/GitHub_Trending/por/portless
本地开发时,localhost 后面的端口号每次重启都变。portless 的本地代理让你直接访问https://myapp.localhost;它的调优靠PORTLESS_*环境变量。
端口号由 portless 在4000–4999区间随机分配,之后一直替你管理,你只需要记住名字。想先了解背景,可以看 README;本文不列清单,而是按一条使用动线讲——本地跑起来 → 手机/局域网访问 → 对外分享 → 旁路与排障——每个变量放在它真正被用到的场景里。
一条命令跑起来:URL 稳定下来,端口交给 portless 管
跑portless myapp next dev时,实际发生三件事:
- portless 先起一个 HTTPS 反向代理,默认占443(纯 HTTP 时为80);
- 你的应用拿到4000–4999里一个随机端口,通过注入的
PORT环境变量知道该监听哪里; - 之后
https://myapp.localhost永远指向这个应用,端口换了你也不用改任何东西。
关键在这里:所有显式PORTLESS_*环境变量优先于portless.json和上次运行记忆的默认值。本地场景最常动的六个变量如下(完整定义见 配置文档):
| 变量 | 它解决的痛点 | 默认值 |
|---|---|---|
| PORTLESS_PORT | 443 需要 root 或被其他服务占着 | 443(HTTPS)/80(HTTP) |
| PORTLESS_HTTPS | CI 或不信任本地 CA 时关掉 HTTPS | 开启 |
| PORTLESS_TLD | 本地域名结构和生产对不上 | localhost |
| PORTLESS_WILDCARD | 想恢复"未注册子域回退父路由" | 严格匹配 |
| PORTLESS_SYNC_HOSTS | 自己管 hosts 文件或环境受限 | 开启 |
| PORTLESS_APP_PORT | 外部工具已经指定了应用端口 | 随机 4000–4999 |
代理端口被占或不想要 sudo:PORTLESS_PORT
443 需要 root 权限,开发机上还常被其他服务占着。给代理换个端口,一行就行:
PORTLESS_PORT=8443 portless proxy start执行后代理改在 8443 上监听,你再也不用为抢 443 加 sudo;写进~/.bashrc就长期生效。
别踩这个坑:非法值(非数字、超出 1–65535)会被静默忽略并回落到协议默认端口,不会报错提醒你,解析逻辑在 cli-utils 实现。
让本地域名结构和生产一致:PORTLESS_TLD
想让本地 URL 长得像线上?TLD 支持单个或多段域名,逗号分隔:
PORTLESS_TLD=localhost,dev.example.com portless执行后你会拿到dev.example.com后缀的 URL;多个 TLD 重叠时按"最长后缀优先"匹配,注入的PORTLESS_URL取第一个 TLD。变更历史见 CHANGELOG。
⚠️ 别把PORTLESS_TLD写成.local——它和 mDNS/Bonjour 冲突;.dev被 Google 持有且有 HSTS 风险。官方推荐 IANA 保留的.test。
CI 里关掉 HTTPS:PORTLESS_HTTPS=0
CI 机器通常没有本地 CA,浏览器也不值得信任它。设PORTLESS_HTTPS=0等价--no-tls,代理切到 80 端口的纯 HTTP,整条链路不再涉及证书。
外部工具已经指定端口:PORTLESS_APP_PORT 的优先级链
有些工具链或团队约定已经给应用定好端口,这时别让 portless 随机分:
PORTLESS_APP_PORT=3100 portless myapp next dev启动后子进程会收到PORT=3100,应用监听这个固定端口而不是随机端口。
应用端口的取值优先级(高到低):--app-port参数 >PORTLESS_APP_PORT>package.json的"portless"键 >portless.json> 自动分配,规则见 配置文档。取值必须是 1–65535,否则启动报错退出,校验在 cli.ts。
子域严格匹配与 /etc/hosts 同步的取舍
默认是严格路由:api.myapp.localhost不会悄悄回退到myapp.localhost。设PORTLESS_WILDCARD=1可恢复"未注册子域回退父路由"的宽松行为。
另一件默认行为:portless 会自动同步/etc/hosts,目的是让 Safari 这类不走浏览器内置.localhost解析的环境也能打开页面。如果你自己管理 hosts 文件或处于受限环境,设PORTLESS_SYNC_HOSTS=0即可旁路,实现见 hosts.ts。
让手机直接打开开发页:LAN 模式的两个变量
真机调试时,手机和电脑不在同一个 loopback 上,myapp.localhost根本到不了手机。PORTLESS_LAN=1让代理绑定0.0.0.0/::,并通过 mDNS 发布<name>.local主机名——同一 Wi-Fi 下的手机直接访问https://myapp.local就能看到你的开发页:
PORTLESS_LAN=1 portless执行后每次启动默认进 LAN 模式(写进 shell 配置就是长期行为)。
VPN、多网卡会让自动探测拿错 IP,这时用PORTLESS_LAN_IP固定:
PORTLESS_LAN_IP=192.168.1.50 portless执行后代理会绑定你指定的这块网卡地址,探测逻辑被跳过。
💡 portless 会"记住"LAN 模式:停止后重启仍是 LAN 模式;临时想切回.localhost,只需单次加PORTLESS_LAN=0。
把开发页发给同事和公网:Tailscale、Funnel、ngrok 三个开关
演示、联调、外部同事要看你的本地应用,又不想改框架配置(比如basePath)?三个变量都是设1生效:
设1后的效果 | 应用里读到的 URL 变量 | 前提 |
|---|---|---|
| PORTLESS_TAILSCALE:在 Tailscale 内网(tailnet)分享给所有设备 | PORTLESS_TAILSCALE_URL | tailscale CLI 已安装、已连接并开启 HTTPS 证书 |
| PORTLESS_FUNNEL:通过 Tailscale Funnel 暴露公网(隐含 tailscale) | PORTLESS_TAILSCALE_URL | 同上 |
| PORTLESS_NGROK:通过 ngrok 暴露公网 | PORTLESS_NGROK_URL | ngrok CLI 已配置 authtoken |
PORTLESS_FUNNEL=1 portless myapp启动后portless list会同时显示本地 URL 和分享 URL;应用退出时隧道自动清理,不用手动收尾。
一句话:把开关写进~/.bashrc或.env即全局生效,不用每条命令都拖着。
应用代码怎么读自己的 URL:portless 注入的变量
portless 启动子进程时自动注入一批变量,应用代码直接读就行,不用硬编码地址:
| 注入的变量 | 内容 | 典型值 |
|---|---|---|
PORTLESS_URL | 应用的主 URL | https://myapp.localhost |
PORT | 应用应监听的临时端口 | 4321 |
HOST | 通常为127.0.0.1(Expo LAN 模式下省略) | — |
NODE_EXTRA_CA_CERTS | portless 本地 CA 路径(HTTPS 时) | — |
PORTLESS_TAILSCALE_URL | Tailscale 地址(启用分享时) | https://dev.tail1234.ts.net |
PORTLESS_NGROK_URL | ngrok 地址(启用时) | https://abc.ngrok.app |
注入逻辑在 cli.ts。
📌 典型用法——OAuth 回调地址不再硬编码:
const baseUrl = process.env.PORTLESS_URL || "http://localhost:3000";这样回调地址在本地、LAN、隧道三种环境下都正确。完整集成方式见 OAuth 技能说明。
不想走代理:PORTLESS=0 旁路的边界在哪
除了带星号的变量,还有一个隐藏主角——不带星号的PORTLESS:
PORTLESS=0 pnpm dev执行后命令原样直接运行,完全绕开 portless 代理,原始输出直接可见。适用场景:调试代理本身、跑vite build这类不需要路由的任务、或临时想看未经加工的输出。
边界要分清楚:portless proxy、portless clean这类子命令不受旁路影响,始终正常执行;子命令的退出码会原样透传。行为见 旁路测试 与 命令参考。
一台机器跑两套代理:用 PORTLESS_STATE_DIR 隔离状态
把代理装成开机自启服务时,环境变量会被固化进服务定义,这就给"双份代理"留了口子:
PORTLESS_STATE_DIR=~/.portless-lan PORTLESS_LAN=1 portless service install装完后这套服务的所有状态文件(routes.json、proxy.pid等)都落在~/.portless-lan,默认那套仍住在~/.portless,实现见 service.ts。
这样同一台机器可以同时跑"日常.localhost代理"和"独立 LAN 代理"两套状态,互不干扰。
常用组合命令卡与动手前自查清单
⚠️ 最常见的情况先说:改了环境变量但代理没变化?已有代理正在运行时,portless 会警告并要求你先portless proxy stop——新变量只对新启动的代理生效。
常用组合,按需复制:
PORTLESS_PORT=8443 portless proxy start PORTLESS_APP_PORT=3100 portless myapp next dev第一条给代理换端口免 sudo,第二条固定应用端口。
PORTLESS_LAN=1 portless PORTLESS_FUNNEL=1 portless myapp第一条进 LAN 模式给手机用,第二条开 Funnel 公网分享。
PORTLESS_STATE_DIR=~/.portless-lan PORTLESS_LAN=1 portless service install PORTLESS=0 pnpm dev第一条装一套独立状态的 LAN 服务,第二条临时旁路代理。
动手前自查五条:
PORTLESS_PORT非法值会被静默忽略(回落 443/80);PORTLESS_APP_PORT非法值会报错退出。两者取值都必须是 1–65535 的数字。- 优先级:显式环境变量 >
portless.json> 上次运行记忆。怀疑配置冲突时,先确认 shell 里没有同名变量。 PORTLESS_TLD避开.local(撞 mDNS)和.dev(Google 持有、HSTS 风险),推荐.test。- 开分享前确认
tailscaleCLI(已连接、HTTPS 证书)或ngrokCLI(authtoken)就绪,否则隧道起不来。 - 代理在跑时改变量无效——先
portless proxy stop,再带着新变量启动。
【免费下载链接】portlessReplace port numbers with stable, named local URLs. For humans and agents.项目地址: https://gitcode.com/GitHub_Trending/por/portless
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考