301・302・307・308 リダイレクトの違い:恒久か一時か、POST が GET になる理由

HTTP リダイレクト 301・302・303・307・308 の違いを RFC 9110 と WHATWG Fetch 標準で整理しました。POST が GET に変わる条件や、キャッシュと検索エンジンでの扱いの違いも解説します。

開発Webネットワーク

フォームを POST で送ったのに、サーバーのログには新しいアドレスへの GET しか残っておらず、本文も空になっている。そんなときは、途中で返ったリダイレクトのステータスコードをまず確認してみてください。301 や 302 を受け取ったクライアントは POST を GET に変えてリクエストし直してよいことになっています。ブラウザのリクエストに適用される Fetch のアルゴリズムと、curl のデフォルトの -L 動作では、POST に対してこの変換が行われます。307 と 308 ではメソッドは変わりません。

2つの問いで4つのコードを分ける

HTTP の仕様である RFC 9110 は、301、302、307、308 を「このリソースは Location ヘッダーが示す別の URI にあるかもしれない」という種類のリダイレクトとしてまとめています。4つのコードは次の2つの問いで区別できます。

  • 移動は恒久的か、一時的か
  • リダイレクトを自動でたどるとき、リクエストメソッドを変えてよいか
POST→GET への変更を許す メソッドを維持
恒久 301 Moved Permanently 308 Permanent Redirect
一時 302 Found 307 Temporary Redirect

HTTP リダイレクトのステータスコードの選択表。横軸は POST を GET に変えてよいコードとメソッドを維持するコード、縦軸は恒久的な移動と一時的な移動です。恒久的な移動では 301 Moved Permanently が POST を GET に変えてよく、308 Permanent Redirect はメソッドを維持します。一時的な移動では 302 Found が POST を GET に変えてよく、307 Temporary Redirect はメソッドを維持します。その下に別枠で 303 See Other があります。303 は移動ではなく、別の URI にある間接的な応答を指し、GET か HEAD で取得します。フォームの POST 後の結果ページが代表的な使い方です。

図:コードを選ぶための簡略化した選択表です。キャッシュや検索エンジン、クライアントの実装による違いと例外は本文で説明します。

301 と 308 は、リソースに新しい恒久的な URI が割り当てられたので、今後はその URI を使うべきだという意味です。302 と 307 は、リソースが一時的に別の URI にあるだけなので、クライアントは今後も元の URI にリクエストするべきだという意味です。

メソッドについては 307 の規定がもっとも明確です。ユーザーエージェントがリダイレクトを自動でたどるとき、リクエストメソッドを変えてはなりません(MUST NOT)。RFC 9110 の 308 の節には同じ文はありません。その代わり仕様は、307 と 308 を「メソッドを維持するリダイレクトであることを曖昧さなく示すために」追加されたコードだと説明し、301 の節でも、メソッドが変わるのを望まない場合は 308 を使うよう案内しています。

301 と 302 で POST が GET に変わる理由

RFC 9110 の 301 と 302 の節には、「歴史的な理由により」ユーザーエージェントは次のリクエストのメソッドを POST から GET に変えてもよい(MAY)という注記があります。

仕様が説明する経緯はこうです。HTTP/1.0 では、301 と 302 はもともとメソッドを維持するコードとして定義されていました。しかし初期のユーザーエージェントは、POST をそのまま POST で送り直すか、別のサイトへ移るときにより安全な GET に変えるかで分かれ、広く普及した慣行は GET に変える方向にまとまりました。そこで、メソッドを確実に維持する 307 と 308 が後から追加され、301 と 302 の定義は POST を GET に変えることを許す形に調整されました。

つまり、301 や 302 でメソッドが変わるのは仕様違反ではなく、仕様が認めている選択肢です。変えないことも認められており、どちらになるかはクライアントの実装が決めます。

303 は「結果は別の場所から取得する」

303 See Other は、上の4つとは性質が異なります。元のリソースが移動したという意味ではなく、リクエストに対する間接的な応答を別のリソースから取得せよという意味で、ユーザーエージェントはその URI に GET か HEAD でリクエストします。RFC 9110 によれば、303 は主に POST の処理結果を別のリソースへ送り、その結果を個別に識別したり、ブックマークしたり、キャッシュしたりできるようにするために使われます。フォームを処理したあと結果ページを GET で開かせたいのであれば、302 よりも 303 のほうがその意図を正確に表せます。

ブラウザのルール:Fetch 標準

ブラウザがリダイレクトをたどる手順は、WHATWG Fetch 標準の「HTTP-redirect fetch」に定められています。この標準は 301、302、303、307、308 をリダイレクトステータスと定義し、次の場合にリクエストメソッドを GET に変えて本文を空にします。

  • ステータスが 301 または 302 で、メソッドが POST のとき
  • ステータスが 303 で、メソッドが GET でも HEAD でもないとき

307 と 308 は、このメソッド変更の条件には含まれません。そのため、リダイレクトをたどれるリクエストでは、メソッドと本文を GET と空の本文に変更しません。この条件から、301 や 302 でも PUT や DELETE にはこの変換が適用されないことがわかります。

ただし、すべてのリクエスト本文を必ず再送できるという意味ではありません。同じ Fetch の手順では、303 以外のリダイレクトでリクエスト本文を再送できない場合、ネットワークエラーになります。

同じ手順にはほかの規則もあります。リダイレクト回数が 20 に達するとネットワークエラーで終わり、リクエストが別のオリジンへ移ると Authorization ヘッダーが削除されます。RFC 9110 も、クライアントは循環するリダイレクトを検出して対処すべき(SHOULD)だとし、以前の仕様(RFC 2068)が最大5回を推奨していたため、そうした固定の上限を持つクライアントがありうると補足しています。

curl で確かめる

curl の -L(--location)も同じ方向で動作します。curl のマニュアルによれば、POST リクエストが 301、302、303 の応答を受けると次のリクエストを GET で送り、それ以外の 3xx ではメソッドを変えません。301 と 302 でも POST を維持したい場合は --post301、--post302 オプションを使います。

ローカルで確かめました。/from/<コード> へのリクエストをそのステータスコードで /to にリダイレクトし、/to は受け取ったメソッドと本文をそのまま返す小さな Python サーバーを立て、macOS の curl 8.7.1 でリクエストしました。

for c in 301 302 303 307 308; do
  printf "$c -> "; curl -s -L -d 'x=1' http://127.0.0.1:8765/from/$c
done
printf "301 --post301 -> "; curl -s -L --post301 -d 'x=1' http://127.0.0.1:8765/from/301

実際の出力です。

301 -> GET /to body=b''
302 -> GET /to body=b''
303 -> GET /to body=b''
307 -> POST /to body=b'x=1'
308 -> POST /to body=b'x=1'
301 --post301 -> POST /to body=b'x=1'

この出力は上記の環境(macOS、curl 8.7.1)で一度実行した結果で、テストサーバーのコードはここには載せていません。ほかのバージョンの curl やほかの HTTP クライアントでは結果が異なることがあるので、使っている環境で実際に確認してください。

キャッシュと検索エンジンでの違い

恒久と一時の違いはキャッシュにも表れます。RFC 9110 は 301 と 308 を「ヒューリスティックにキャッシュ可能」な応答に分類しています。明示的な有効期限の情報がなくても、キャッシュが有効期間を推定して応答を再利用できるという意味です。302 と 307 はこの分類に入りません。いずれの場合も、Cache-Control のような明示的なキャッシュ指示があればそれに従います。そのため、元に戻す可能性のある移動に 301 を使うと、すでに応答をキャッシュしたクライアントが、戻したあとも古いリダイレクトを再利用することがあります。戻す可能性があるなら、一時的なコードを使うか、キャッシュ指示を明示しておくほうが安全です。

検索エンジンは HTTP の仕様ではなく、それぞれのポリシーに従います。Google 検索セントラルのドキュメント(2026年4月14日更新)によると、Google は 301 と 308 を恒久的なリダイレクトとみなし、リダイレクト先の URL を正規(canonical)とするシグナルとして使います。302、303、307 は一時的なリダイレクトとみなし、そのシグナルとしては使いません。ただし、ほかの正規化シグナルがあれば、リダイレクト先のページがインデックスされることもあるとしています。

どれを使うか

  • ドメインの移転や、複数のアドレスを1つにまとめる場合のように移動が恒久的なら、301 か 308 を使います。POST を受ける API のアドレスを移したなら、メソッドが維持される 308 が適しています。
  • サービスが一時的に使えないあいだ案内ページへ送るような一時的な移動なら、302 か 307 を使います。メソッドと本文を維持する必要があるなら 307 です。
  • フォームの処理後に結果ページを GET で開かせたいなら 303 を使います。

最後にもう1つ。308 は 2014年6月の実験的 RFC で初めて定義されており、ほかのコードよりかなり新しいため、RFC 9110 はすべての環境で認識されるとは限らないと注記しています。古いクライアントまでサポートする必要があるなら、そのクライアントで実際に確認してください。

参照した仕様とドキュメント

  • RFC 9110 — HTTP Semantics(2022年、STD 97):15.4 節のリダイレクトのステータスコード(301、302、303、307、308)とその経緯についての注記、15.1 節のヒューリスティックにキャッシュ可能なステータスコード。この記事でいう HTTP のルールの基準です。
  • RFC 7538 — The Hypertext Transfer Protocol Status Code 308 (Permanent Redirect)(2015年):308 の以前の定義。現在は RFC 9110 に置き換えられています。
  • WHATWG Fetch Standard — HTTP-redirect fetch(Living Standard):ブラウザのリクエスト処理における、リダイレクトステータスの定義、メソッド変更の条件、リダイレクト回数の上限、オリジンが変わるときのヘッダーの扱い。
  • curl man page:-L、--post301、--post302 オプション。1つのツールの実装ドキュメントで、バージョンによって変わることがあります。
  • Google 検索セントラル — リダイレクトと Google 検索(2026年4月14日更新):Google 検索が各リダイレクトをどう扱うか。1つの検索エンジンのポリシーです。

リンクと内容は 2026年9月29日に確認しました。Fetch は随時更新される標準で、Google のドキュメントも変わることがあります。

ブログへ