◆ パスを変更してプロキシするとヘッダーやレスポンスボディのパスってどうなるの
  ◆ 流石に自動でいい感じになったりはしない模様
  ◆ パスっぽい文字列でも勝手に書き換えると困るところもありえるし
◆ 自分でリクエスト前後で処理するしかなさそう
◆ どこが書き換えるべきパスか考えるのも面倒だし 最終的なアプリケーション側でユーザがアクセスした URL を基にパスを作るほうが楽
◆ だけど X-Forwarded 系ではユーザがアクセスしたフルの URL は送られないみたい
◆ 調べてるついでに気になった Koa の proxy 系ミドルウェアはいいのがなかった

プロキシ時のパス

これまでプロキシ関係は Node.js 用に nginx のリバースプロキシを使うくらいでした
複雑な書き換えは一切なくて origin 部分だけ書き換えた URL にアクセスするくらいです

client
↓ (https://server/foo/bar)
nginx
↓ (http://localhost:8000/foo/bar)
node.js

みたいなものです
パス以降はそのままです

ですがパスを書き換えたいこともあります

複数サーバを 1 つのサーバから公開するために最初のディレクトリ名ごとにサーバにプロキシしたり

https://server/foo/* → http://foo/*
https://server/bar/* → http://bar/*

反対に一部のディレクトリのみ公開するためにルートアクセスを内部用サーバの深い階層に対応させたり

https://public/* → http://private/foo/bar/*

こういうことをするときのパス対応ってどうするのでしょうか?
以前は面倒なことをしたくなくて公開されてないサーバ側のディレクトリ構造も公開側に合わせたことがありました

https://server/foo/* → http://foo/foo/*

パス的にはそのままなのでパスについて考える必要はないです
ただ foo の方のサーバではすべて foo ディレクトリの中に配置しないといけなくなりますし server 側で /foo/bar にプロキシするパスが変わると foo サーバ側のパスも /foo/bar に変更しないといけません
毎回公開する側に合わせられるものとも限りません

パスマッピング

さすがにできないわけはないので 各ツールでの設定でパスの書き換えはできました
ただ リクエストするパスはできてもその他の部分ってどうするのでしょうか?

リクエストでもヘッダーにパスが埋め込まれているかもしれません
レスポンスはヘッダーもそうですしボディの HTML や JSON 中にパスが埋め込まれているかもしれません

オプション類を探してみても リクエスト時のマッピング以外はほとんどのところで見つかりませんでした
知名度のあるものだと リダイレクトや Cookie のパスについてのオプションはありましたがそれくらいです
考えてみると リクエストする URL 以外だとどこに書き換えるべきデータがあるかもわからないですし 文字列的にパスっぽいのが来ただけだと書き換えてはいけないものかもしれません
フレームワークのミドルウェアやプラグインみたいなものだとプロキシ先へのリクエスト送信前やレスポンス受信後に任意の処理を追加できるのでそこで自由に書き換えてってことでしょう
nginx や apache みたいなのだとどうするんでしょうね

アプリケーション側で制御する?

できそうと言っても パスが含まれるところすべてを見つけて 修正の必要性を確認して必要なら修正するって結構面倒なことです
修正自体も JSON なら場所が分かれば修正はまだ楽そうですが HTML の場合を考えるととても辛そうです
JavaScript や CSS ファイルなどのリソースファイル中にもパスが入ってる可能性があります
アプリケーション側を作った人でも全部のパスが含まれるところなんて確実に把握してないでしょうし それ以外の人がプロキシを用意する場合もありえます

最初からアプリケーション側で作るときにパスを管理するモジュールを用意して パス出力は全部それを通すようにしておき ユーザがアクセスした URL を参考にパスを生成するようにしておいたほうがいい気がしました

https://server/foo/* → http://foo/*

のプロキシだと foo サーバ側への /bar/baz というアクセスは ユーザ的には /foo/bar/baz でアクセスしてるので /bar へのリンクは /foo/bar というパスで出力するという感じです
プロキシ側ではレスポンスのパスは意識しなくて済みます

これまでも origin 付きで URL を生成するときには X-Forwarded-Host などの情報を見て プロキシ経由でもユーザがアクセスできる形で URL 生成するのは当たり前のように行っていたので それほど特別なことでもないはずです

いざやってみようとしたらいきなり問題が出ました
X-Forwarded 系って origin 関係の情報とクライアントの IP アドレスしかないようでした
ユーザがアクセスしたフルパスがありません
探すと非標準の X-Forwarded-Prefix というのを付けたり フレームワークが独自の名前で xxx-original-path みたいなヘッダーを追加してそこにフルパスが入っていたりはするようです
Prefix の方はプレフィックスをつけることはできても消すことはできないですし フルパスを入れるのに一般的な名前はないようです
リバースプロキシならともかく クライアント側に設置するプロキシだとパスを外部に教えたくないとかありそうですし 仕方ないのかもしれません

Koa の proxy ミドルウェア

ここからは本題から外れて proxy 関係を調べてたときにあった Koa 関係の話です

リダイレクトくらいはデフォルトでパス書き換えしてくれるのかなと思っていくつかのツールやライブラリを見てました
普段よく使う Koa だとどうなのかなと見ていると そもそも使えそうなの proxy ミドルウェアがこれと言ってない感じでした
以前どこかでも書きましたが 本体は express の後継としてより優れているのにミドルウェアが全然充実してないんですよね

候補として上がるのはこの辺です

https://github.com/popomore/koa-proxy
https://github.com/edorivai/koa-proxy
https://github.com/nsimmons/koa-better-http-proxy
https://github.com/vagusX/koa-proxies

koa-proxy

koa-proxy の 1 つ目の方は昔人気があったもののようです
新しいバージョンに対応していないようですし すでにアーカイブされています
1 つ目の方の README 中に 2 つ目の koa-proxy へのリンクがあるので 2 つ目の koa-proxy は公式に認められたフォークのようです

しかし 2 つ目の koa-proxy ももう数年更新されておらず すでに deprecated となった request パッケージに依存しています
使うべきものではなさそうです

koa-better-http-proxy

better なんて名乗ってますし 最終更新が 1 年ちょっと前なので新しくこれが今のスタンダードかなと思いました
しかし少し見てみただけで問題だらけで実用レベルのものではありませんでした

全く使われていないのに依存関係に winston が入っていてムダにインストールされたり
関係無い express を require するモジュールが含まれていたり
最後に不要な next 呼び出しがあって正しく動作しなかったりです

express はテスト用なんだと思いますが test フォルダがあるのに lib フォルダの方にありました
winston と違い依存関係にはないのでインストールはされてないので 気持ち悪くも無視できるものです

致命的なのは最後の next 呼び出しの方です
ミスなのかミドルウェアの仕組みを理解していないのか ミドルウェアで処理済みなのになぜか最後に next を呼び出してます
そのせいでプロキシでレスポンスを受け取って ctx.body に設定したあとに次のミドルウェアが処理されます
次のミドルウェアで ctx.body が上書きされるので プロキシ先へリクエストするものの その結果を返さないというよくわからないものになっています
次のミドルウェアが存在しない環境でテストしてうまく動いてたとかでしょうか

それなのになぜか Star が 100 超えで 使えるものなのかなと思いましたが全然なクオリティでした

koa-proxies

最後のこれが一番マシなものです
これ自体は大したことをせず中で使ってる http-proxy というパッケージがメインの処理をしています
https://github.com/http-party/node-http-proxy

http-proxy パッケージは npm 全体で見たプロキシ機能を提供するパッケージでもたぶん最大のもので今でも週間 1000 万ダウンロードを超えています
そんな http-proxy を使ってるなら迷うこと無く koa-proxies を使えばいいかと思いましたが このパッケージも問題ありでした

http-proxy はもう長いことメンテナスされておらず メンテナ募集してるような状態です
ソースコードを見ても昔に作られたのが分かる作りで イベントベースで扱いづらいです
一部の機能は非同期処理をサポートしていないので このライブラリでは実現できないこともありえます
すでにこれらの問題は issue/PR で述べられているもののメンテされていないので修正されません

また ドキュメントに例は色々あるものの詳細リファレンス相当のものがないです
簡易なオプション説明はあってもそれを読んだだけではどういう動作になるのかの詳細がわからない部分が多いです
例えば target/forward はどっちも同じ説明になっていて何が違うのか分かりません
ソースコードを見て機能を把握するにも作りが古くて読みづらいです

他フレームワーク

hapi や fastify はフレームワーク公式にこういった処理をするパッケージを持ってます
その方針のほうがフレームワークがサポートされている間は機能に困らないので ミドルウェアを自作しないならやっぱりこういったフレームワークのほうがいいのかもしれません
Koa のミドルウェアは 新しくなにか作るたびに毎回のように困らされるところなので hapi か fastify への移行か自分でよくある機能のミドルウェアを自作してしまうほうがいいかなと思ってます