Node.jsによるコンテンツ変換用リバースプロキシ
2020/02/27更新
目次
基本的なリバースプロキシ
Node.jsでリバースプロキシを作るには、http-proxyモジュールを使うと非常に簡単にできる。
const httpProxy = require('http-proxy');
// 対象のホスト(オリジンサーバー)
const TARGET_HOST = 'http://localhost';
// リクエストを受け付けるポート
const PROXY_PORT = 8080;
const proxy = httpProxy.createProxyServer({target: TARGET_HOST});
proxy.listen(PROXY_PORT);
これで、http://localhost:8080/へアクセスすると、http://localhost/からの応答をプロキシして返すことができる。
リクエストを処理する
HTTPヘッダの操作
オリジンサーバーへリクエストを渡す前にHTTPヘッダを操作したりする必要がある場合は、proxyReqイベントハンドラ内で処理する。
proxy.on('proxyReq', (proxyReq, req, res, options) => {
// ヘッダの追加・修正
proxyReq.setHeader('Host', 'localhost');
// ヘッダの除去
proxyReq.removeHeader('Accept-Encoding');
});
レスポンスを処理する
HTMLの置換
前述の基本的な例では、http://localhost/からの応答をただ単に右から左へと受け流すだけである。そこで、レスポンスのHTMLに一定の変換を施して返すような処理を追加してみる。
proxy.on('proxyRes', (proxyRes, req, res) => {
// 変換対象でないファイルは何も手を加えない
if (!/\.html$/.test(req.url)) {
return;
}
// 本来の出力メソッドを退避しておく
const _write = res.write;
const _end = res.end;
// 対象サーバーからのレスポンスを取得する
let body = Buffer.from('');
res.write = (data) => {
body = Buffer.concat([body, data]);
};
// 変換処理を施して返す
res.end = () => {
// 全ての<p>タグを赤色にする
const output = body.toString().replace(/<p>/g, '<p style="color: #ff0000">'); // ◆
// 変換後のバイト数でContent-Lengthヘッダを更新する
const contentLength = Buffer.byteLength(output);
res.setHeader('Content-Length', contentLength);
// 変換したデータを返す
_write.call(res, output);
_end.call(res);
};
});
これで、例えばhttp://localhost:8080/index.htmlにアクセスすると、http://localhost/index.htmlの<p>タグが全て赤色になって表示される。上記コードの◆の部分を好きな処理に置き換えれば、レスポンスを自由に加工して返すことができる。
圧縮されたレスポンスへの対応
Accept-Encoding: gzipなどのヘッダを付けてオリジンサーバーにリクエストすると、レスポンスが圧縮されている場合がある。その場合は、レスポンスを処理する前にまず解凍する必要がある。Accept-Encodingヘッダを取り除いてリクエストすれば、圧縮されることはなくなる。
const zlib = require('zlib');
…(略)…
proxy.on('proxyRes', (proxyRes, req, res) => {
…(略)…
res.end = () => {
// Content-Encodingヘッダを見て、もしgzip圧縮されていれば解凍する
if (proxyRes.headers['content-encoding'] == 'gzip' && body.length > 0) {
body = zlib.gunzipSync(body);
}
…(略)…
};
});