Node.js プロキシ設定: Axios, Fetch, Undici & ローテーション

axios のプロキシオプションは HTTPS ターゲットで罠になり、ネイティブの fetch はプロキシ環境変数を完全に無視します。ここでは、axios、fetch、undici で機能する設定を紹介します — エージェント、認証、SOCKS5、ローテーション、ストリーミング。

Node.js のリクエストをプロキシ経由でルーティングするのは一行で済むように見えて、デバッグに午後を費やすことになります。その理由は、ほとんどのプロジェクトで使用される 3 つの HTTP クライアント — axios、Node 18 でグローバルとして出荷されたネイティブの fetch、および基盤となる undici ライブラリ — がそれぞれ異なる方法でプロキシを受け取り、期待通りに動作しないからです。axios には組み込みの proxy 設定があり、Node 専用で、HTTP プロキシの背後にある HTTPS ターゲットに対して静かに壊れています。このバグは 2020 年から axios の GitHub トラッカーにオープンされており、Stack Overflow で多くの票を集めたスレッドを引き起こしました。一方、ネイティブの fetch は HTTP_PROXY 環境変数を無視し、プロキシオプションをまったく持っていません。このガイドでは、各クライアントで実際に機能する設定、認証、SOCKS5、ローテーション、ストリーミングを紹介します。

Axios プロキシ設定 vs プロキシエージェント

Axios は proxy オブジェクト — { host, port, auth } — を備えており、プレーン HTTP ターゲットには問題なく機能します。罠は HTTPS にあります: ターゲットが https:// でプロキシが HTTP を話す場合、axios は CONNECT トンネルを開くことができず、リクエストがハングするか、実際の IP で戻ってきます。コミュニティが合意した修正は、組み込みの設定を完全にバイパスし、代わりに axios にプロキシエージェントを渡し、proxy: false を設定して、2 つのメカニズムが競合しないようにすることです:

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

// Note the destructured import — a default import is the classic gotcha.
const agent = new HttpsProxyAgent("http://USER:PASS@gate.quantumproxies.io:PORT");

const client = axios.create({
  httpAgent: agent,   // for http:// targets
  httpsAgent: agent,  // for https:// targets
  proxy: false,       // disable axios' own broken proxy handling
  timeout: 15000,
});

const r = await client.get("https://httpbin.org/ip");
console.log(r.data); // { origin: "<proxy exit IP>" }

2 つの詳細が時間を節約します。まず、分解された { HttpsProxyAgent } インポートを使用します — 最近のバージョンでは名前付きシンボルとしてエクスポートされ、デフォルトのインポートは構築時に例外を投げるオブジェクトを与えます。次に、プロキシ URL スキームはプロキシが HTTPS トラフィックを運ぶ場合でも http:// のままです: スキームはプロキシへの到達方法を説明し、ターゲットへの TLS はトンネル内で実行されます。同じ失敗クラスは Python でも見られます; requests でも作業している場合、パターンは私たちの ProxyError および SSLError 修正ガイド と共鳴します。

認証と環境変数

認証されたプロキシは HTTP ベーシック認証を使用します。エージェントを使用する場合、URL に http://user:pass@host:port として埋め込みます; パスワードに @:/ が含まれている場合は、encodeURIComponent() で URL エンコードして、URL が間違った場所で分割されないようにします。407 Proxy Authentication Required は、資格情報が拒否されたか、ソース IP がホワイトリストに登録されていないことを意味します。axios はまた、HTTP_PROXYHTTPS_PROXYNO_PROXY を環境から読み取ります — サードパーティライブラリをコードに触れずにプロキシするのに便利です — そして、proxy: false を設定することでそれを無効にします。重要な警告: ネイティブの fetch はこれらの変数を読み取りません、したがって、env-var プロキシングに依存するスクレーパーは、axios から fetch に切り替えた瞬間に直接接続されます。

Node.js における axios、ネイティブ fetch、undici がそれぞれプロキシを受け入れる方法の比較、プロキシエージェントと undici ディスパッチャーの対比
同じリクエスト、3 つの配管システム。axios はエージェントを必要とし、ネイティブ fetch は undici ディスパッチャーを必要とし、それらを混ぜることが「プロキシが機能しない」の主な原因です。

ネイティブ fetch を undici でプロキシする

Node のグローバル fetch は undici 上に構築されており、プロキシは undici にあります。ProxyAgent を作成し、非標準の dispatcher オプションとして渡します — これは fetch をプロキシするための現代的で依存性の少ない方法であり、HTTPS CONNECT を箱から出して正しく処理します:

import { ProxyAgent } from "undici";

const dispatcher = new ProxyAgent({
  uri: "http://gate.quantumproxies.io:PORT",
  token: "Basic " + Buffer.from("USER:PASS").toString("base64"),
});

const res = await fetch("https://httpbin.org/ip", { dispatcher });
console.log(await res.json());

グローバル fetch ではなく undici を直接使用している場合、同じ ProxyAgentrequest() または setGlobalDispatcher() にプラグインして、プロセス内のすべての fetch を一度にプロキシします。この単一呼び出しのグローバルスイッチは、ディスパッチャーをすべての関数に通すことなく、コードベース全体をプロキシ経由でルーティングする最もクリーンな方法です。

Node.js における SOCKS5 プロキシ

axios も undici も SOCKS をネイティブにサポートしていません — axios の proxy 設定に socks5:// 文字列を渡すと、protocol mismatch アサーションが発生します。socks-proxy-agent をインストールし、HTTPS エージェントと同じ方法で使用し、httpAgenthttpsAgent の両方に接続します。socks5:// よりも socks5h:// を好む: 末尾の h はプロキシ側で DNS を解決し、DNS リークを防ぎ、出口地点からの地理的に制限されたホスト名を解決します。すべての SOCKS5 プロキシ プランは、HTTP と SOCKS5 の同じゲートウェイを公開しているので、これはスキームの切り替えであり、新しい購入ではありません:

import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";

const agent = new SocksProxyAgent("socks5h://USER:PASS@gate.quantumproxies.io:PORT");

const client = axios.create({ httpAgent: agent, httpsAgent: agent });
const r = await client.get("https://httpbin.org/ip");
console.log(r.data);

プロキシリストなしのローテーション

古いレシピ — IP の配列、リクエストごとの Math.random()、死んだものを剪定 — はもはや維持しないコードです。ローテーションゲートウェイは、リクエストごとにサーバー側で新しい出口 IP を割り当てるので、1 つのエンドポイントがプール全体のように振る舞います。ローテーション住宅プロキシを通じて、そのプールは 200 以上の国にわたる 90M+ の IP をカバーし、以下のループはゼロローテーションロジックで各反復ごとに異なるオリジンを印刷します:

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

const agent = new HttpsProxyAgent("http://USER:PASS@gate.quantumproxies.io:PORT");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false });

for (let i = 0; i < 3; i++) {
  const r = await client.get("https://httpbin.org/ip");
  console.log(r.data.origin); // a different exit IP each time
}

フローが複数のリクエストにまたがる場合 — ログイン、カートに追加、チェックアウト — リクエストごとのローテーションはセッションを壊します。プロキシユーザー名のスティッキーセッションパラメータは、設定されたウィンドウの間に 1 つの出口 IP を固定し、その後ローテーションします。同じエンドポイント、1 つの文字列変更。これを数千の同時リクエストにスケールする場合、axios ループから作業を移し、大規模スクレイピングアーキテクチャのガイドを読んで、キューイングと同時実行予算を確認してください。

プロキシを通じてストリーミングレスポンス

ファイルや大きな JSON ペイロードをダウンロードするのは、エージェントがアタッチされた後はどのリクエストと同じです — responseType: "stream" を設定し、ボディをディスクにパイプします。プロキシは転送を透過的に処理するので、200MB のエクスポートがメモリにバッファリングされることはありません:

import fs from "node:fs";

const r = await client.get("https://example.com/large.json", {
  responseType: "stream",
});
r.data.pipe(fs.createWriteStream("out.json"));
Node.js プロキシ設定のチェックリスト、axios、fetch、SOCKS5 設定を壊す一般的な間違いとの対比
ほとんどの Node.js プロキシの失敗は 5 つの間違いに帰着します: axios の HTTPS バグ、デフォルトエージェントのインポート、二重プロキシ設定、間違った SOCKS スキーム、fetch が環境変数を読み取ることを期待すること。

よくある質問

なぜ私の axios プロキシが機能しないのですか?

最も一般的な原因は、HTTP プロキシの背後にある HTTPS ターゲットです: axios の組み込み proxy 設定は CONNECT トンネルを開くことができず、実際の IP を返すか、ハングします。プロキシエージェントに切り替え — HttpsProxyAgenthttpAgenthttpsAgent にアタッチし、axios がプロキシを自分で処理しようとするのを止めるために proxy: false を設定します。

axios は SOCKS5 プロキシをサポートしていますか?

ネイティブにはサポートしていません — socks5:// URL を proxy 設定に渡すとプロトコルミスマッチエラーが発生します。socks-proxy-agent をインストールし、SocksProxyAgent を構築し、httpAgenthttpsAgent の両方に接続します。socks5h:// スキームを使用して、DNS がマシンから漏れるのではなくプロキシ側で解決されるようにします。

Node.js でネイティブ fetch にプロキシを使用するにはどうすればよいですか?

ネイティブ fetch にはプロキシオプションがなく、HTTP_PROXY 環境変数を無視します。undici の ProxyAgent を作成し、fetch 呼び出しで dispatcher オプションとして渡すか、setGlobalDispatcher() を一度呼び出してプロセス内のすべての fetch をプロキシします。undici は追加の設定なしで HTTPS CONNECT を正しく処理します。

環境変数で axios プロキシを設定するにはどうすればよいですか?

完全なプロキシ URL で HTTP_PROXYHTTPS_PROXY、およびオプションで NO_PROXY をエクスポートします; axios はそれらを自動的に読み取ります。リクエストまたはインスタンスで proxy: false を設定して、axios が環境を無視するようにします。これは axios にのみ影響することを忘れないでください — undici とネイティブ fetch はそれらの変数を拾いません。

全体のストーリーはカードに収まります: axios はエージェントと proxy: false を必要とし、ネイティブ fetch は undici ディスパッチャーを必要とし、SOCKS は独自のエージェントを必要とし、ローテーションはループではなくゲートウェイに属します。配管を正しく行えば、最後の変数は IP の品質です — クリーンな住宅出口は、同じコードで 403 を受け取るフラグ付きデータセンター IP を通過します。

最初のリクエストで通過する住宅プロキシを取得