🐈

Node.js の fetch でリダイレクトが処理される流れを追う

に公開

前置き

この記事では、Node.jsでfetchでリダイレクトが発生した際に、最終的なレスポンスを取得できる仕組みを紹介します。(本記事では低レイヤの実装については踏み込みません)

実際の動作

検証に利用するサイト

検証には httpbin.org を利用します。このサイトでは、https://httpbin.org/redirect/{n}にアクセスすると、n-1のURLにリダイレクトされます。https://httpbin.org/redirect/1からはhttps://httpbin.org/getにリダイレクトされ、以下のようなJSONを返します

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", 
    "Accept-Encoding": "gzip, deflate, br, zstd", 
    "Accept-Language": "ja,en-US;q=0.9,en;q=0.8", 
    "Host": "httpbin.org", 
    "Priority": "u=0, i", 
    "Sec-Ch-Ua": "\"Chromium\";v=\"140\", \"Not=A?Brand\";v=\"24\", \"Google Chrome\";v=\"140\"", 
    "Sec-Ch-Ua-Mobile": "?0", 
    "Sec-Ch-Ua-Platform": "\"macOS\"", 
    "Sec-Fetch-Dest": "document", 
    "Sec-Fetch-Mode": "navigate", 
    "Sec-Fetch-Site": "none", 
    "Sec-Fetch-User": "?1", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    "X-Amzn-Trace-Id": "Root=1-68c7df48-047fce2c44ee4efa2b864286"
  }, 
  "origin": "175.177.48.22", 
  "url": "https://httpbin.org/get"
}

また、curl -ihttps://httpbin.org/redirect/1叩くと、302リダイレクトのレスポンスが返ることが確認できます。

$ curl -i https://httpbin.org/redirect/1

HTTP/2 302 
date: Mon, 15 Sep 2025 09:53:43 GMT
content-type: text/html; charset=utf-8
content-length: 215
server: gunicorn/19.9.0
location: /get
access-control-allow-origin: *
access-control-allow-credentials: true

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/get">/get</a>.  If not click the link.%  

Node.jsでの実行結果

それではコードとその実行結果を確認してみます。
コード:

const res = await fetch("https://httpbin.org/redirect/1");
console.log(await res.text());

実行結果:

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "br, gzip, deflate", 
    "Accept-Language": "*", 
    "Host": "httpbin.org", 
    "Sec-Fetch-Mode": "cors", 
    "User-Agent": "node", 
    "X-Amzn-Trace-Id": "Root=1-68c7e13a-45737a1033105b8e6135fda7"
  }, 
  "origin": "175.177.48.22", 
  "url": "https://httpbin.org/get"
}

この結果から、Node.js の fetch はリダイレクトを自動的に処理してくれていることが分かります。

Node.jsの実装

それでは、この挙動がどのようにNode.jsで実装されているかを確認していきます。Node.jsのfetchは内部的にundiciというライブラリに依存しています。fetchのエントリーポイントはこの行になります。
https://github.com/nodejs/node/blob/f1a8f447d7363e9a5e1c412c1a425a9771bc691f/deps/undici/src/lib/web/fetch/index.js#L130

このfetchからfetchingが呼ばれます。

https://github.com/nodejs/node/blob/f1a8f447d7363e9a5e1c412c1a425a9771bc691f/deps/undici/src/lib/web/fetch/index.js#L366

さらにこのfetchingからmainFetchが呼ばれ、

https://github.com/nodejs/node/blob/f1a8f447d7363e9a5e1c412c1a425a9771bc691f/deps/undici/src/lib/web/fetch/index.js#L520

httpFetchが呼ばれます。httpFetchの以下の部分でレスポンスを取得しています。(本記事ではこれ以上の深掘りは行いません)

https://github.com/nodejs/node/blob/main/deps/undici/src/lib/web/fetch/index.js#L1137

そして、httpFetchの以下の部分でリダイレクトの処理を行っています。

https://github.com/nodejs/node/blob/main/deps/undici/src/lib/web/fetch/index.js#L1171-L1199

redirectStatusSetはHTTPの仕様に基づいた値が設定されています。

https://github.com/nodejs/node/blob/main/deps/undici/src/lib/web/fetch/constants.js#L8-L9

ここでは、リクエスト時にリダイレクトモードがmanualであり(errorまたはfollowでない、かつHTTPステータスコードが30x系の場合にhttpRedirectFetchが呼ばれるようになっています。このhttpRedirectFetchからはLocationで指定されたURLでmainFetchが呼ばれるので、fetchはリダイレクトした値を取得できるようになっています。

実装まとめ

  1. fetchfetchingmainFetchhttpFetch の順に処理が進む
  2. httpFetch でレスポンスを取得する
  3. レスポンスのステータスコードが リダイレクト対象 (30x 系) であれば httpRedirectFetch が呼ばれる
  4. httpRedirectFetch では Location ヘッダーの URL を使って再度 mainFetch を実行する

まとめ

Node.jsのfetchではHTTPステータスコードに応じて再リクエストを行うので、リダイレクト先のレスポンスを取得できるという仕組みでした!

Discussion