JavaScriptでHTTPリクエストを行う2つの方法については、延々と議論が交わされていますが、その議論はたいてい、最も重要度の低い側面で行われています。
バンドルサイズ、構文の洗練度、依存関係の正当性――これらはあくまで好みの問題であり、合理的な人々でも意見が分かれるところです。 しかし、ある違いは単なる好みの問題ではなく、本番コードで実際のバグを引き起こすものです: fetch は、サーバーがエラーステータスを返してもそのプロミスを破棄しません。404でも解決され、500でも解決されてしまいます。その結果、.catch() は実行されず、コードはすべてが正常に動作したかのように処理が進んでしまいます。
これは単なる癖ではなく、ドキュメントに記載された動作であり、何よりもまず理解しておくべき点です。
私たちは Geonode であり、プロキシを販売しているため、本題に関連する注記をここに記します:これら2つの間には、Nodeにおけるプロキシのサポートに関して、真に存在しながらも十分に文書化されていない違いがあり、これが頻繁に利用者を困惑させています。 Node.jsのネイティブfetchは、標準的なプロキシ環境変数を認識しません。これは、curlと同じように動作すると想定しているほぼすべての人を驚かせます。これについては別のセクションで説明していますが、リクエストをプロキシ経由でルーティングしていない場合は、そのセクションを完全にスキップしても構いません。ほとんどの人にとってはそうすべきでしょう。
古いリンクをたどるすべての人に影響するため、ちょっとした注意点を記しておきます:Axiosのドキュメントは移動しました。 axios-http.com は現在、axios.rest にリダイレクトされます。古いドメインを指すブックマークやStack Overflowの回答は、リダイレクトを経由して引き続き機能しますが、正規の場所は変更されています。
以下に述べる動作に関する内容はすべて、誰かの記憶に基づくものではなく、MDNおよびAxios自身のドキュメントに基づいています。
実際のバグを引き起こす違い
ここから読み始めてください。なぜなら、これは単なる好みの問題ではない唯一の違いだからです。
MDNの解説
ドキュメントからの直接引用:
「fetch()
プロミスは、リクエストが失敗した場合(例えば、リクエストURLの形式が不正である場合やネットワークエラーが発生した場合など)にのみrejectされます。 fetch()
のPromiseは、サーバーがエラーを示すHTTPステータスコード(404
、504
など)で応答した場合でも、rejectはしません。その代わりに、then()
ハンドラは、Response.ok
および/またはResponse.status
プロパティを確認する必要があります。」
しませんという強調はMDNによるものです。
実務上、それが意味すること
// Looks correct. Is not.
try {
const res = await fetch('/api/user/999');
const user = await res.json();
showUser(user); // runs on a 404
} catch (err) {
showError(err); // never runs on a 404
}
サーバーはエラー本文を含む 404 を返しました。fetch
は問題なく解決されました。res.json()
はエラーオブジェクトを解析しました。showUser
はユーザーではない何かを受け取り、その失敗は後で別の場所で、未定義のプロパティに関する分かりにくいエラーとして表面化します。
正しいバージョン:
try {
const res = await fetch('/api/user/999');
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const user = await res.json();
showUser(user);
} catch (err) {
showError(err);
}
3行追加されましたが、これらはあなたが書くすべてのリクエストで必須となります。
Axiosの動作
Axiosはデフォルトで、2xx範囲外のステータスコードを拒否します。これと同等のコードではステータスチェックは不要です:
try {
const { data } = await axios.get('/api/user/999');
showUser(data);
} catch (err) {
showError(err); // runs on a 404
}
どちらの挙動が正しいか
どちらも正当な主張であり、意見の相違は哲学的なものです。
fetch
は、転送は成功したという立場を取っています。つまり、サーバーに到達し、応答があり、レスポンスは完全な状態で届いたのです。 404は質問に対する有効な回答であり、質問ができなかったことによる失敗ではありません。これを拒否することは、トランスポートの失敗とアプリケーションのセマンティクスを混同することになります。ちなみに、これはcurlの立場とも完全に一致しており、curlでも404に対しては出口コード0が返されます。
Axiosは、ほとんどのリクエスト発信者が4xxや5xxを失敗として扱うため、それに見合った振る舞いをするべきだという立場をとっています。
現実的には、fetch
のモデルの方がより正確である一方で、エラーが発生しやすいと言えます。なぜなら、すべての呼び出しにおいて厳格な管理が求められ、その管理が怠られた場合に警告してくれる仕組みがないからです。
Fetchとは何か、そしてそのコスト
この標準規格には、利点もあれば、欠点もある。
利点
組み込み機能である。 依存関係がなく、インストールも不要で、バンドルコストもかからず、監査すべきサプライチェーンもない。ブラウザや最新のNode.jsでは、単に最初から備わっている。
標準仕様です。 方向性を変えたり、放棄されたり、独自のスケジュールで互換性を損なう変更を導入したりする可能性のあるプロジェクトによって維持されるのではなく、仕様として定義されています。
基盤となります。 多くの高レベルなライブラリはこれをラップしたものであるため、fetchを理解することは、それらの動作を理解することにつながります。
ストリーミング処理に優れています。 Response.body は読み取り可能なストリームであるため、大規模なレスポンスの段階的な処理が自然に可能です。
できないこと
これらはユーザー自身が補う必要がある機能であり、その数の多さこそが、Axiosを採用する本当の理由です。
JSONの自動判別機能はありません。 .json() を呼び出す必要がありますが、これはもう1つの await であり、レスポンスがJSONでない場合(例えば、設定ミスによるサーバーから返されるHTMLエラーページなど)に失敗するもう1つのポイントです。
ステータスによる拒否機能はありません。 上記で説明した通りです。
デフォルトではタイムアウト機能なし。 fetch は無期限にハングアップする可能性があります。AbortSignal.timeout()は最新の環境ではタイムアウト機能を提供しますが、これはオプションであり、忘れがちです。ハングアップが最も深刻な状況においてこそ、これが最も重要な問題となります。
インターセプターなし。 認証トークン、相関ID、またはロギングを一元的に添付する場所がありません。 すべての呼び出しでこれを繰り返すか、ラッパーを記述する必要がありますが、ラッパーを記述する過程で、人々はうっかり自分たちでより劣ったAxiosを作り上げてしまうのです。
アップロードの進行状況表示がない。 レスポンスストリームを介してダウンロードの進行状況を確認することは可能ですが、アップロードの進行状況は簡単には確認できません。
リクエストボディの自動シリアライズ機能がない。 JSON.stringifyを呼び出し、毎回自分でコンテンツタイプを設定する必要があります。
Nodeでのプロキシ設定機能なし。 これについては後述のセクションで説明します。
率直なまとめ
fetchは、よく設計された低レベルのプリミティブです。その機能の欠落は意図的なものです。標準は最小限であり、特定の立場を押し付けないものであるべきだからです。
問題は、それが優れているかどうかではありません。欠けているレイヤーを自分で実装したいかどうか、そして、実装したバージョンが、大規模なユーザーベースを持ち、エッジケースを洗い出しているプロジェクトによってメンテナンスされているものよりも優れているかどうかです。
Axiosがもたらすもの
公式ドキュメントにある通り、その機能一覧こそが最大の魅力です。
環境を問わず一貫したインターフェースを備えたPromiseベースのHTTPクライアントで、ブラウザ用とNode用のバンドルが別々に提供されています。
リクエストおよびレスポンス用のインターセプター。これは最も価値のある機能であり、かつクリーンに再現するのが最も難しい機能でもあります。認証ヘッダーを一度設定し、401リフレッシュを一度処理し、ロギングを一度追加するだけで、アプリケーション内のすべてのリクエストがそれを継承します。
双方向での自動JSON処理。 リクエストボディはシリアライズされ、コンテンツタイプが設定されます。レスポンスはresponse.dataにパースされます。
エラー処理:2xx以外のステータスコードに対してリクエストを拒否します。
タイムアウト設定:ドキュメントでは「リクエストが無限にハングアップするのを防ぐ」と説明されています。呼び出しごとにアボートコントローラーを用意するのではなく、1つの設定値で済みます。
進行中のリクエストのキャンセル。
アップロードおよびダウンロードの進捗追跡。これは、fetchでは直接提供されていない機能です。
XSRF保護が組み込まれています。
ファイルの投稿およびマルチパートフォームデータが自動的に処理されます。
レート制限およびリクエストのスロットリング。
デフォルト設定を持つインスタンス。これにより、ベースURL、ヘッダー、タイムアウトが設定されたクライアントを一度作成すれば、どこでもインポートできます。
コスト
依存関係。 インストール、更新、監査が必要なものです。セキュリティ意識の高い環境では、これは理論上のコストではなく、現実的なコストとなります。
バンドルサイズ。 小規模なフロントエンドでは意味がありますが、大規模なアプリケーションやサーバーサイドの処理では無視できる程度です。
習得すべき新たな抽象化であり、時折、基盤となるプラットフォームとは異なる挙動を示し、予想外の結果をもたらすことがあります。
公平な視点
Axiosは、こうした動作が必要になった際に、多くの人がfetchの上に構築することになるものに近いものです。ただし、すでに記述済みで、デバッグ済みであり、あなたが考えもしなかったケースもすでに処理済みである点が異なります。
もしこれらの機能が一切必要ないなら、それは無駄な依存関係となります。
比較表
| fetch | Axios |
|---|
| インストール | 組み込み | npm install |
| 404/500でのリクエスト拒否 | なし | あり |
| JSONの解析 | 手動 .json() | 自動 |
| リクエストボディのシリアライズ | 手動 | 自動 |
| タイムアウト | AbortSignal.timeout() | 設定オプション |
| インターセプター | なし | あり |
| アップロードの進捗表示 | 簡単ではない | あり |
| ダウンロードの進捗表示 | レスポンスストリーム経由 | あり |
| キャンセル | AbortController | 標準機能 |
| XSRF 保護 | 手動 | 標準機能 |
| デフォルト設定のインスタンス | なし | あり |
| Node でのプロキシ設定 | なし | あり |
| ストリーミング | 優れている | 制限が多い |
| バンドルコスト | ゼロ | 小さいがゼロではない |
同じリクエスト、2つの方法
// fetch, written correctly
const res = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ name: 'Alice' }),
signal: AbortSignal.timeout(5000)
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// Axios
const { data } = await axios.post(
'https://api.example.com/users',
{ name: 'Alice' },
{ headers: { Authorization: `Bearer ${token}` }, timeout: 5000 }
);
どちらも正しい。fetch版は11行、Axios版は5行だが、その差はすべて、一度設定するのではなく、呼び出しのたびに覚えておかなければならない事項によるものである。
決定的な2つの行
404/500時のリジェクトとNodeでのプロキシ設定は、一方のツールでは他方のツールが直接行うことを単純には実行できない唯一の行です。残りは定型コードであり、定型コードは不可能なことではなく、コストに過ぎません。
定型コードのオーバーヘッド
真の比較対象は、fetch
の呼び出しと axios
の呼び出しの単体比較ではありません。それぞれを使用しているコードベース同士の比較なのです。
誰もが最終的に書くコード
ステータスチェック、タイムアウト、JSONのパースを3回か4回繰り返した後、あなたは次のようなコードを書くことになるでしょう:
async function request(url, options = {}) {
const res = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
...options.headers
},
signal: options.signal ?? AbortSignal.timeout(options.timeout ?? 10000)
});
if (!res.ok) {
const body = await res.text();
throw new HttpError(res.status, body);
}
return res.status === 204 ? null : res.json();
}
これは合理的で、かなり優れたラッパーです。また、紛れもなく、小さなAxiosでもあります。
誰かが気づくまで、このラッパーが処理できないこと
バックオフを伴うリトライ。複数の呼び出しが同時に失敗した際、リクエストストームを起こさずに401エラーでトークンを更新すること。JSONではなくHTMLを返すリクエスト。ボディのない204レスポンス。ネストされた呼び出しを通じて伝播されるキャンセル。アップロードの進行状況。JSON以外のコンテンツタイプ。トレース用の相関ID。
それぞれは小さな追加機能に過ぎません。 これらを総合するとライブラリとなりますが、あなたが作成するバージョンは、すでに何千人もの人が使用しているバージョンほど十分にテストされていないでしょう。
自分で書くのが適切な場合
必要な機能がごくわずかである場合。 1つのAPIに対する数件のGETリクエスト。上記のラッパーは30行で、完全に自分のものになります。
バンドルのサイズが本当に重要な場合。 1キロバイト単位で競合が生じる、パフォーマンスが極めて重要なページ。
依存関係にコストがかかる場合。 すべてのパッケージにレビューが必要な環境。
プラットフォームを理解したい場合。 正当な理由であり、その理解は他の場面にも活かされます。
そうではない場合
ラッパーの改修が3回目になっている場合、コードベースの異なる部分に異なるラッパーが存在する場合、あるいはリトライロジックを追加している場合。その時点で、あなたはサイドプロジェクトとしてライブラリを維持管理していることになり、回避した依存関係の方が、自作したものよりもコストが低くなります。
バンドルサイズとノードの問題
答えを変える2つの文脈的要因。
ブラウザ内
Axiosはバンドルのサイズを増加させます。それが問題になるかどうかは、構築しているものによって完全に異なります。
読み込み時間が「製品」そのものであるランディングページやウィジェットの場合は、fetchを使用してください。 1キロバイト単位でも重要であり、リクエストパターンは通常シンプルなので、定型コードの記述も容易です。
大規模なアプリケーションで、すでにフレームワークやコンポーネントライブラリを導入している場合――追加コストは無視できる程度であり、インターセプターのサポートがもたらす価値は、そのバイト数よりもはるかに大きいと言えます。
率直に言えば、バンドルサイズは、美的な好みを技術的な理由として正当化したいときに人々が持ち出す議論に過ぎません。確かに存在はしますが、引用される頻度に比べて、決定的な要因となることははるかに少ないのです。
Node.js の場合
計算の仕方は全く異なります。サーバー上ではバンドルサイズは関係ありません。したがって、Axios に反対する主な論点は消滅します。
最新のNodeではネイティブのfetchが利用可能で、問題なく動作します。しかし、サーバーサイドのコードでは、fetchが省略している要素、すなわちあらゆる操作に対するタイムアウト、バックオフ付きのリトライ、一元化された認証処理、アウトバウンド呼び出しの構造化されたロギング、そして——次のセクションで取り上げる——プロキシ設定などが、まさに必要とされる傾向があります。
したがって、サーバー側ではブラウザよりもAxiosに軍配が上がるが、これは通常議論される内容とは正反対である。
誰も言及しない妥協案
両方を使うことも可能だ。単純な呼び出しにはfetchを、仕組みが必要な場面ではAxiosを使う。それを禁じるものは何もなく、一貫性に関する議論も、聞こえほど説得力はない。
真に問題を引き起こすのは、1つのコードベース内に3つの異なる独自開発のラッパーが存在し、それぞれがエラー処理をわずかに異なる方法で処理していることです。これは、どちらかのライブラリを一貫して使用する場合よりも悪く、驚くほど多くのプロジェクトが最終的にこの状態に陥っています。
両方のプロキシ
ここが我々の専門分野であり、多くの開発者にとって実に困惑する午後をもたらした原因でもあります。
概要
Node.jsのネイティブfetchは、標準のプロキシ環境変数を読み取りません。 HTTP_PROXY や HTTPS_PROXY を設定しても何の効果もありません。これはcurlやほとんどのHTTPライブラリとは異なり、また、ほぼすべての人が期待していることとも異なります。
Node.jsのグローバルなfetchはundiciを基盤としており、プロキシ経由でのルーティングには、ディスパッチャーを明示的に指定する必要があります:
import { ProxyAgent, setGlobalDispatcher } from 'undici';
setGlobalDispatcher(new ProxyAgent('http://user:pass@proxy.example.com:8080'));
// now fetch goes through the proxy
const res = await fetch('https://example.com');
あるいは、リクエストごとにdispatcherオプションを指定することで設定できます。
Node.js での Axios
Axios には、proxyという設定オプションがあります:
const res = await axios.get('https://example.com', {
proxy: {
protocol: 'http',
host: 'proxy.example.com',
port: 8080,
auth: { username: 'user', password: 'pass' }
}
});
SOCKS プロキシの場合、あるいはよりきめ細かな制御を行う場合、一般的なアプローチは、httpAgentおよび httpsAgentとしてエージェントライブラリを渡すことです。
ブラウザでは、どちらもできません
時間を節約できるため、述べておく価値があります。 ブラウザ上のJavaScriptではプロキシを設定できません。 ブラウザはシステムや拡張機能で設定されたプロキシを使用し、どのライブラリでもこれを変更することはできません。AxiosのproxyオプションはNode.js固有の機能です。
ブラウザベースのコードからプロキシ経由のリクエストを行う必要がある場合は、リクエストを自身が管理するサーバーを経由させる必要があります。
デバッグのヒント
Nodeでプロキシが「機能していない」場合は、他のことを調べる前に、どのクライアントがリクエストを行っているかを確認してください。Axiosでは動作するがfetchでは失敗するコード、あるいはその逆のケースは、ほぼ必ずこの原因によるものです。どちらのエラーも発生しないため、この問題は見過ごされがちです。リクエストは単に直接送信されているだけです。
設定済みのクライアントを通じてアドレス報告エンドポイントにリクエストを送信し、返されたアドレスがプロキシのものかどうかを確認することで、これを検証してください。
そして、自らの利益に反する部分
ほとんどのサーバーサイドのHTTP呼び出しには、プロキシは全く必要ありません。 アクセスが許可されたサーバーから、認証情報を持つAPIを呼び出す場合、特別な設定は一切必要ありません。プロキシは遅延や障害の原因となり、コストも発生します。プロキシが真価を発揮するのは、地理的な制限の確認や、アドレスごとのレート制限が適用される大規模なデータ収集の場面です。それ以外の場合、通常のクライアントの方が適しています。
どちらを選ぶべきか
結論というよりは、判断材料のリストです。
フェッチを使うべき場合
バンドルのサイズが本当に重要な場合。 ランディングページ、ウィジェット、埋め込みスクリプトなど。
リクエストが単純な場合。 GETリクエストが数件で、エラー処理は最小限、共有認証がない場合。
依存関係を追加できない場合、あるいはすべてのパッケージにレビューが必要な場合。
ストリームを扱う場合。 fetch のストリーミングモデルの方が優れており、これは単なる好みの問題ではなく、真の技術的優位性です。
プラットフォームを学びたい場合。 知識は転用できますが、Axios固有の知識は転用できません。
以下の場合はAxiosを使用してください
インターセプターが必要な場合。 一元化された認証、トークンの更新、ロギング、相関IDなど。これが最も強力な理由であり、fetchにはこれに匹敵する明確な代替手段がありません。
サーバー側で開発している場合。 バンドルサイズは問題にならず、欠けている機能こそがサーバーサイドコードに必要なものです。
アップロードの進行状況が必要な場合。fetchではこれを簡単には実現できません。
大規模なコードベース全体で多種多様なリクエストを行い、ラッパーを維持することなく一貫した動作を望む場合。
プロキシ設定が必要で、ディスパッチャーを組み立てるよりも、ドキュメント化されたオプションを使用したい場合。
どちらを選んでも
必ずタイムアウトを設定してください。 AbortSignal.timeout() または Axios の timeout オプションを使用します。タイムアウトを設定しないリクエストは無限にハングアップする可能性があり、これは JavaScript の HTTP コードにおいて最も一般的な信頼性の欠陥です。
必ず fetch でステータスを確認してください。 例外なく、すべての呼び出しで。res.ok はたった2語ですが、これを省略することが、この記事の冒頭で取り上げたバグの原因です。
一元化してください。 1つのラッパー、または1つの設定済みAxiosインスタンス。1つのコードベースに3つの一貫性のないアプローチが存在することは、どちらのライブラリを使うよりも悪く、誰も意図的に選ぶことのない結果です。
よくある質問
Axiosとfetchの主な違いは何ですか?
エラー処理です。MDNによると、fetchのPromiseは「サーバーがエラーを示すHTTPステータスコードで応答した場合でもrejectしません」— response.ok を自分で確認する必要があります。Axiosは2xx以外のステータスコードでrejectします。それ以外は利便性の問題です。Axiosには、インターセプター、自動JSON変換、タイムアウト、進行状況の追跡、プロキシ設定などの機能が追加されています。
fetchが組み込まれた今、Axiosはまだ必要か?
必要な機能次第です。単純なリクエストであれば、必要ありません。しかし、インターセプター、アップロードの進行状況、インスタンスごとのデフォルト設定、Nodeのプロキシ設定などについては、Axiosは依然としてfetchにはない機能を提供しており、これらを自分で実装しようとすると、小さなライブラリのメンテナンスが必要になります。
なぜ fetch は 404 エラーをスローしないのか?
転送が成功したためです。つまり、サーバーに到達し、応答が返ってきたからです。fetch は 404 をリクエストの失敗ではなく有効なレスポンスとして扱い、ネットワークエラーや形式の不正な URL の場合にのみリクエストを拒否します。すべての呼び出しで response.ok を確認してください。
Axiosとfetch、どちらが速いですか?
単一のリクエストの場合、その差は無視できる程度です。どちらもネットワークの速度に左右されます。Axiosはわずかな処理負荷が加わり、ブラウザ上ではライブラリ自体のダウンロードにもわずかな時間がかかります。
fetchでタイムアウトを設定するにはどうすればよいですか?
AbortSignal.timeout(5000) 最新の環境では、signalオプションとして渡すか、独自のタイマーを含むAbortControllerを使用します。デフォルトのタイムアウトは存在しないため、タイムアウトを設定しないリクエストは無限に待機し続ける可能性があります。
Node.jsでfetchはプロキシと連携しますか?
通常の環境変数では連携しません。Node.jsのグローバルなfetchはundiciに基づいており、HTTP_PROXYやHTTPS_PROXYを読み取りません。 ProxyAgent ディスパッチャーを、グローバルに setGlobalDispatcher で指定するか、リクエストごとに明示的に指定する必要があります。一方、Axios には proxy という設定オプションがあります。
ブラウザで fetch を使用する際、プロキシは使えますか?
いいえ。ブラウザの JavaScript ではプロキシを設定できません。ブラウザはシステムまたは拡張機能の設定を使用します。ライブラリのプロキシオプションは、Node 専用の機能です。 ブラウザからのプロキシ経由のリクエストは、自身が管理するサーバーを経由する必要があります。
1つのプロジェクトでAxiosとfetchの両方を使用すべきですか?
それ自体は問題ではありません。真の問題となるのは、エラーの挙動が異なる、一貫性のない独自開発のラッパーが複数存在する場合です。リクエストを生成したライブラリがどれであるかよりも、失敗時の処理方法の一貫性の方が重要です。
まとめ
違いの一つは本質的なものであり、残りは好みの問題です。
** fetch は HTTP エラーを理由にリクエストを拒否しません。** MDN では次のように明示されています:404 や 504 は解決され、response.ok または response.status を自分で確認する必要があります。 1回の呼び出しで見逃すと、その失敗は全く別の場所で、そもそも有効ではなかったデータに関する紛らわしいエラーとして表面化してしまいます。Axiosはデフォルトで2xx以外のステータスコードをリジェクトします。どちらの立場も正当化できますが、すべての呼び出しにおいて厳格な対応を必要とするのは一方だけであり、締め切りが迫る中でコードベースがそのような厳格さを維持し続けることは困難です。
それ以外のすべては、あなたが何を構築しているかにかかっています。 ブラウザ上では、fetchが組み込まれており無料で利用でき、単純なリクエストパターンであれば定型コードもごくわずかです。サーバー上では、バンドルのサイズは問題にならなくなり、fetchが省略している機能――タイムアウト、インターセプター、リトライ、プロキシ設定――こそがまさにサーバーコードに必要なものなので、バランスは逆の方向に傾きます。
試してみる価値のあるテスト:もし fetch をラップするコードを書き、その行数が30行を超えているなら、あなたは小さなHTTPライブラリを維持していることになります。 これは、意図的に行われた正当な選択である場合もあれば、偶然に生じた不適切な選択である場合もあります。
プロキシについてですが、これは人々が午後を費やしてしまう部分です。Node.jsのネイティブfetchは、標準のプロキシ環境変数を無視します。HTTPS_PROXYを設定しても何の効果もなく、リクエストは直接送信され、エラーも発生しません。undiciのProxyAgentディスパッチャーを指定するか、Axiosのproxyオプションを使用してください。そして、単に推測するのではなく、アドレスを報告するエンドポイントに対して検証を行ってください。
当社はプロキシを販売していますが、サーバーサイドのリクエストのほとんどはプロキシを必要としません。プロキシが必要な場合、どのクライアントを使用しているかを把握することは、デバッグの最終段階ではなく、最初のステップとなります。