NodeJSでプロキシを設定する

NodeJSでプロキシを設定する


最近、あるプロジェクトで NodeJS にプロキシを使用する必要がありました。最初は単純な設定だと思っていましたが、意外と手間がかかりました。

NodeJS はデフォルトでシステムプロキシを読み取らず、また NodeJS にはネットワークリクエストを送信する方法が複数あるため、プロキシの設定方法も様々です。以下にまとめます。

前提条件

プロキシサーバーのアドレスは環境変数から取得すると仮定します。これは多くの Web アプリケーションの標準的な方法です。便宜上、これらをHTTP_PROXYHTTPS_PROXYと呼びます。

Fetch

最近の NodeJS は Fetch インターフェースをサポートしており、ブラウザの Fetchとの一貫性を保とうとしていますが、完全に一貫性を保つとプロキシを設定する方法がなくなってしまいます。幸いなことに、NodeJS はdispatcherという独自のパラメータを追加して、プロキシサーバーを含むネットワークリクエストパラメータを変更できるようにしました。

undiciパッケージを使用してdispatcherを設定できます:

npm install undici

fetch プロキシの設定

ProxyAgent を使用して設定します:

import { ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  await fetch(url, { dispatcher: new ProxyAgent(proxyUrl) });
}

グローバル fetch プロキシの設定

setGlobalDispatcherは、NodeJS プログラム内のすべての fetch 呼び出しのプロキシを設定するために使用されます:

import { setGlobalDispatcher, ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  setGlobalDispatcher(new ProxyAgent(proxyUrl));
}

プロキシを削除するには、空の Agent を設定できます:

import { Agent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new Agent());

仕組み

setGlobalDispatcherは実際に NodeJS の隠れたエントリーポイントを使用しています。NodeJS のグローバルオブジェクトには、グローバルディスパッチャーを設定するための Symbol があります:

global[Symbol.for("undici.globalDispatcher.1")] = yourDispatcher;

これは NodeJS のドキュメントには記載されていないようです(少なくとも私は見つけられませんでした)。詳細については、https://github.com/nodejs/node/issues/43187 を参照してください。

プロキシ設定以外の dispatcher の機能

ProxyAgentの代わりにAgentをインポートすると、keepAlive や timeout などのネットワークパラメータも設定できます:

import { Agent } from "undici";

const res = await fetch("https://example.com", {
  dispatcher: new Agent({
    keepAliveTimeout: 10,
    keepAliveMaxTimeout: 10,
  }),
});
const json = await res.json();
console.log(json);

詳細については、undici のドキュメントを参照してください。

NodeJS ネイティブモジュール http と https

上記のように、NodeJS にはプロキシを設定するための組み込みメソッドがないため、http.Agenthttps.Agentを使用して開発する必要があります。現在、人気のあるパッケージはhttp-proxy-agenthttps-proxy-agentです。

https-proxy-agent の使用例:

import https from "https";
import { HttpsProxyAgent } from "https-proxy-agent";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  const agent = new HttpsProxyAgent(proxyUrl);

  https.get(
    {
      host: "example.com",
      path: "/",
      agent,
    },
    (res) => {
      // レスポンスの処理
    }
  );
}

Axios

Axios は JavaScript の業界標準と言っても過言ではありません。プロキシを設定する方法は複数あります(優先度の低い順):

  1. 環境変数:Axios 自体が環境変数HTTP_PROXYHTTPS_PROXYを読み取ります

  2. proxyパラメータ:

    import axios from "axios";
    
    axios.get("https://example.com", {
      proxy: {
        host: "127.0.0.1",
        port: 7890,
      },
    });
  3. httpAgenthttpsAgentパラメータ:NodeJS の組み込みhttp.Agenthttps.Agentを渡して、ネットワークパラメータを制御できます。

    前のセクションで言及したhttp-proxy-agenthttps-proxy-agentを使用します:

    import axios from "axios";
    import { HttpsProxyAgent } from "https-proxy-agent";
    import { HttpProxyAgent } from "http-proxy-agent";
    
    const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
    if (proxyUrl) {
      const httpsAgent = new HttpsProxyAgent(proxyUrl);
      const httpAgent = new HttpProxyAgent(proxyUrl);
    
      const instance = axios.create({
        httpAgent,
        httpsAgent,
      });
    }

詳細については、Axios ドキュメントを参照してください。

HTTPS 接続の制限

最初の 2 つの方法で HTTP プロキシを設定すると、Axios は HTTPS 接続を確立できないことがわかりました。その理由は、Axios が CONNECT メソッドを送信しないため、TLS 接続を確立できないことです(CONNECT メソッドについて理解していない場合は、この記事を読んでください)。