Portlessの仕組み
localhost:3000の代わりにmyapp.localhost — ポート番号を名前に置き換えるプロキシ
ローカル開発はポート地獄だ。localhost:3000、localhost:3001、localhost:5173... 何番が何だったか覚えられないし、AIエージェントに「3000番に接続して」と言っても次に変わったらまた教えなければならない。
Portlessはこの問題を根本的に解決する。portless myapp next devで実行すればhttp://myapp.localhostでアクセス可能。
コアアーキテクチャ:名前 → ポートマッピングプロキシ
ブラウザ Portless Proxy (443) 開発サーバー
myapp.localhost ──────→ routes.json参照 ──────→ localhost:4231
api.myapp.localhost ──→ サブドメインマッチ ──→ localhost:4582
実行フロー:portless myapp next dev
プロキシデーモン確認 —
~/.portless/proxy.pidを読み取り。なければ自動起動(デタッチされた子プロセス)。システムサービスではなく初回使用時に自動起動。ポート割り当て — 4000〜4999の範囲でランダムポート探索。
net.createServer().listen()で実際にバインドテスト。フレームワーク検出 + ポート注入
Next.js、Express、Remix →
PORT環境変数をネイティブに認識Vite、Astro、Angular →
PORTを見ないフレームワーク。--port、--hostフラグをCLI引数に自動挿入。
ルート登録 —
routes.jsonに{ hostname: 'myapp', port: 4231, pid: 12345 }を記録子プロセス実行 —
PORT=4231で実行。終了時にルート自動削除。
プロキシ内部
TCPソケットの最初の1バイトをpeekしてTLS/平文HTTPを1つのポート(443)で分岐。
ルーティング:ホスト名完全一致 → サブドメイン一致。x-portless-hopsヘッダーでループ検出(5回以上で508エラー)。
.localhostが機能する理由
.localhostはRFC 2606予約TLD。Chrome/Firefox/Edgeは*.localhostを/etc/hostsなしで127.0.0.1に自動解決。Safariのみhostsファイル同期が必要(portlessが自動処理)。
自動HTTPS
- 自己署名CA(EC、10年有効期限)
- SNIコールバックでホスト名ごとの証明書を動的生成
portless trustでシステムキーチェーンにCA登録NODE_EXTRA_CA_CERTSを子プロセスに注入
ファイルベースの状態管理
routes.jsonは毎リクエスト再読み込み(キャッシュなし)。ディレクトリ作成によるアトミックミューテックス。PID生存チェックでゾンビルート自動GC。
Worktreeサポート(v0.5.2+)
git worktreeのブランチを検出してホスト名の先頭に付加:feat-auth.myapp.localhost
動作フロー
CLIがプロキシデーモンの存在確認 → なければdetachedプロセスで自動起動(ポート443)
4000〜4999の範囲で空きポート割り当て + フレームワーク別ポート注入(PORT envまたは--portフラグ)
routes.jsonに{ hostname, port, pid }登録 → プロキシが毎リクエストこのファイルを読んでルーティング
TCP最初の1バイトpeek(0x16 = TLS)でHTTPS/H2 vs 平文HTTPを分岐 — 1ポートで両方処理
SNIコールバックでホスト名別証明書を動的生成 + キャッシュ。自己署名CAをシステム信頼ストアに登録
子プロセス終了時にroutes.jsonから自動削除 + PID生存チェックでゾンビルートGC
メリット
- ✓ ポート番号を覚える必要なし — 名前ベースURL
- ✓ HTTPS + HTTP/2自動 — 証明書生成・インストールまでワンステップ
- ✓ フレームワーク不問 — Next.js、Vite、Express、Nuxtなど全対応
- ✓ 常時起動ではない — 初回使用時自動起動、システムサービス登録不要
- ✓ Worktree + サブドメイン対応でブランチ別独立アクセス
デメリット
- ✗ ポート443使用時にmacOS/Linuxでsudo必要
- ✗ Safariは*.localhostの自動解決未対応 → /etc/hosts同期が必要
- ✗ routes.jsonを毎リクエスト読み込み — 超高頻度リクエスト時にI/O負荷の可能性
- ✗ Dockerコンテナ内部では追加設定が必要(PID強制指定)
- ✗ Node.js 20+必須、Windows未対応