301,302,303リダイレクトはPOSTメソッドがGETに書き換わる

Locationヘッダ値のURIへ向けてブラウザがリダイレクトを行う際、HTTPステータスコードによってはリクエストメソッドが変更されることがある。

環境

検証

挙動を確認してみる。redirect.phpは、POSTメソッドでフォームからデータを送信後、そのデータを自身で受け取ってheader関数でresult.phpへリダイレクトさせる。この転送時に任意のステータスコードを指定する。

<?php
//redirect.php
if ($_POST) {
    //ステータスコード301の場合
    header('Location: result.php', true, 301);
}
?>
<!DOCTYPE html>
<title>redirect</title>
<form method="post">
<input type="submit" name="foo" value="bar">
</form>
<?php
//result.php
var_dump($_SERVER['REQUEST_METHOD'], $_POST);
//結果
//301, 302, 303
string(3) "GET"
array(0) {
}

//307, 308
string(4) "POST"
array(1) {
  ["foo"]=>
  string(3) "bar"
}

301~303のステータスコードを受け取ったブラウザはメソッドをPOSTからGETへ書き換える……と同時に、データ(['foo' => 'bar'])を消失させてしまう。これは、POSTで送信されたデータはリクエストボディに格納されるが、GETメソッドはこのボディ部分を持たないため。一方、メソッド変更のない307や308の場合にはデータもそのまま転送されていることが分かる。

POSTをGETに変更するこのブラウザの振る舞いはRFCに適合したものだが、かつて(2014年のRFC7231まで)は誤りとされていたようだ。

参考