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); } …(略)… }; });