この記事の目次
古い案内ページから新しい案内へ転送したつもりなのに、ブラウザーでは「転送が多すぎます」と出る。あるいは最終的に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までお願いします。