公開・運用公開 約8分

HTTPリダイレクトを到着先まで追う:連鎖・ループ・相対URLを調べる

転送を設定したのに目的のページへ着かないときに。ローカルのHTTPサーバーとcurlで、各応答のLocationと最終URLを照合します。転送の循環と、相対URLが別の階層へ解決される失敗を分けて直します。

この記事の目次
  1. 最初の302は、目的のページが存在する証拠ではない
  2. 正常な連鎖と、二種類の失敗を同じサーバーへ置く
  3. 同じURLが再登場したら、上限より循環を直す
  4. Locationの先頭スラッシュで、到着する階層が変わる
  5. 一時転送の教材を、そのまま本番の方針にしない

古い案内ページから新しい案内へ転送したつもりなのに、ブラウザーでは「転送が多すぎます」と出る。あるいは最終的に404へ着く。最初のURLだけを見ても、途中でどこへ進んだかは分かりません。各応答を一行ずつ並べると、直すべき転送を絞れます。

URLのパス、ターミナル、JavaScriptの基本が分かる人向けです。Node.js 24系とcurlを使い、架空の施設案内をローカルだけで再現します。GETの転送が対象で、ログインやフォームのPOST、実サイトの設定変更は行いません。

最初の302は、目的のページが存在する証拠ではない

HTTPリダイレクトでは、サーバーが3xxの応答とLocationという移動先を返します。ブラウザーなどのクライアントがそのURLへ新しい要求を送り、先にも転送があれば続けます。最初の転送設定が正しくても、到着先の内容が正しいとは限りません。

たとえば/old → /middle → /guideなら、oldとmiddleは別々の応答です。guideが200でも、それが目的の利用案内かは本文で照合します。MDNの転送の説明を踏まえ、「転送された」と「用件に合うページへ着いた」を分けます。

要求したパス 応答 Location 判断
/old 302 /middle まだ途中
/middle 302 /guide まだ途中
/guide 200 なし 本文を確認する段階

古いURLに何を残すかは移行先の対応表の記事で決めます。今回は決めた移行先へ、実際のHTTP応答がたどり着くかを確認する作業です。

正常な連鎖と、二種類の失敗を同じサーバーへ置く

空の練習フォルダにredirects.mjsを保存します。Node.js標準のHTTP APIを使い、外部パッケージは不要です。127.0.0.1だけで待ち受けるため、この練習を公開サーバーへ配置する必要はありません。

import { createServer } from 'node:http';
const redirects = new Map([
  ['/old', '/middle'],
  ['/middle', '/guide'],
  ['/loop-a', '/loop-b'],
  ['/loop-b', '/loop-a'],
  ['/nested/start', 'guide']
]);
createServer((req, res) => {
  const pathname = new URL(req.url, 'http://127.0.0.1:8772').pathname;
  if (req.method !== 'GET') {
    res.writeHead(405, { Allow: 'GET' }).end();
    return;
  }
  if (redirects.has(pathname)) {
    res.writeHead(302, {
      Location: redirects.get(pathname),
      'Cache-Control': 'no-store'
    }).end();
    return;
  }
  res.writeHead(pathname === '/guide' ? 200 : 404, {
    'Content-Type': 'text/plain; charset=utf-8',
    'Cache-Control': 'no-store'
  });
  res.end(pathname === '/guide' ? '利用案内:入口は北側です。\n' : '案内が見つかりません。\n');
}).listen(8772, '127.0.0.1', () => console.log('http://127.0.0.1:8772'));

node redirects.mjsで起動し、別のターミナルで次を実行します。最初のコマンドは転送を追わず、応答ヘッダーだけを見ます。-IはHEADになるため、このGETだけの教材では使いません。

curl -sS -D - -o /dev/null http://127.0.0.1:8772/old
curl -sS -L --max-redirs 5 -D - http://127.0.0.1:8772/old

一つ目は302とLocation: /middleを返します。二つ目は302、302、200のヘッダーと、最後の「入口は北側です」という本文を表示します。-Lは転送追跡、-D -はヘッダーを標準出力へ出す指定です。オプションはcurl公式マニュアルで確認できます。

最終結果を短く残すときは、次も使えます。本文を省いているので、これだけで内容まで合格にしないでください。

curl -sS -L --max-redirs 5 -o /dev/null \
  -w 'final=%{url_effective} redirects=%{num_redirects} status=%{http_code}\n' \
  http://127.0.0.1:8772/old

期待する値はfinalが末尾/guide、redirectsが2、statusが200です。転送の回数を減らすならoldの移動先を/guideへ直します。再起動後にredirectsが1へ減り、本文と最終URLが同じままなら、用件を保って連鎖を短くできています。

同じURLが再登場したら、上限より循環を直す

/loop-aを-L付きのコマンドへ渡すと、loop-a、loop-b、loop-aと戻ります。最大5回まで追った後、curlは上限へ達したエラーで止まり、終了コードは47になります。これは200の正常な到着ではありません。

このとき--max-redirsを大きくするのは修正になりません。同じ二つの設定が相手へ送り返しているため、回数を増やしても循環が続きます。まずLocationの並びで同じ絶対URLが再登場する位置を探します。

修正では、教材のloop-bの移動先を/guideへ変更します。サーバーをCtrl+Cで止めて起動し直し、loop-aを再確認します。302、302、200で終わり、入口の案内が読めれば循環を切れています。loop-aとloop-bの両方を一度に変えず、一方の出口で直る理由を説明してください。

実サイトではHTTPとHTTPS、末尾スラッシュの有無など、別の設定層が互いに戻す場合もあります。比較にはパスだけでなく、スキーム、ホスト、ポートを含むURLを残します。ただし今回の教材で、そうした本番構成まで検証したとは扱いません。

Locationの先頭スラッシュで、到着する階層が変わる

次に/nested/startを追います。Locationがguideなので、要求先URLを基準に解決した到着先は/nested/guideです。教材にそのページはなく、404になります。転送回数は1でも、目的の/guideへは着いていません。

修正は、移動先を'/guide'へ変えることです。同じサーバーの先頭からのパスとして解決されます。再起動して、最終URLが/guide、statusが200、本文が入口案内になるか照合します。パスの考え方は相対リンクの記事と共通ですが、ここではHTMLのhrefではなく、サーバーのLocationを直します。

転送先へクエリーを引き継ぐかも、目的に合わせた別の判断です。このサーバーはpathnameだけで振り分け、指定したLocationへ送るため、元の検索条件を維持する例ではありません。引き継ぐべき条件がある移行で、そのままコピーして消してしまわないようにします。

一時転送の教材を、そのまま本番の方針にしない

ここでは再実行しやすいよう302とno-storeに固定しています。恒久的な移転なら301や308などを検討しますが、キャッシュやHTTPメソッドの扱いも含めて選びます。特にPOSTの転送をGETの教材から推測しないでください。

内部リンクが既に新しい行き先を指せるなら、入口から旧URLを経由させる必要も見直します。一方、過去に共有された旧URLへの転送は別に必要なことがあります。「連鎖を短くする」と「旧URLを消す」を同じ変更にしません。

15分程度の確認では、最初の/oldが二回で200、loop-aが上限で停止、nested/startが404という三条件を記録します。修正後はoldが一回、loop-aが二回、nested/startが一回で同じ/guideへ到着するのが解答です。公開前の無料レッスンでURLを点検するときも、最終ステータスだけでなく、本文の目印まで一致させて確認を終えられます。

TRY A LESSON

記事の内容を、課題で確かめる

公開・運用の手順を考える練習 →

公開・リリースの学習内容 →

記事についてのお問い合わせ

記載内容へのご質問や訂正のご連絡は、hello@junkbranding.comまでお願いします。

← 記事一覧に戻る