【php】リロード対策!postを重複しないように自分自身にリダイレクトさせて解決しよう!

この記事からわかること
- POSTの重複送信の防ぎ方
- リロード対策
- header関数によるリダイレクト処理
- リダイレクトさせる場所とコツ
index
[open]
\ アプリをリリースしました /
チャット機能を作ろうとしている時にpostで送信したデータがリロードするたびに送信されてしまうのを防ぐ方法を模索していた時の解決策をまとめたいと思います。
リロード対策方法
処理の終わりにheader関数で自分自身にリダイレクトさせる
header("Location:./chat.php");
exit;
結論を先に書いておくと処理の最後にページをリダイレクトさせることでページのリロード時のpostの二重送信を防ぐことができます。
それではなぜこうなるのかとリダイレクトさせる時のコツを解説していきたいと思います。
フォーム送信で起きる二重送信
フォーム機能を作り$_POSTで入力された値はページを更新するとそのPOSTの値が消えずに再度サーバーに送信されてしまい2回同じことが起きてしまいます。
私が実際にプログラムを作るときの状況を交えながら流れを見ていきます。
プログラムの流れ
- チャットを入力⇨送信
- $_POSTで受け取りエスケープ処理
- データベースに格納
- データベースにあるチャット履歴をある分だけ表示
今回作ったのはよくあるチャット機能です。チャットを入力するとデータベースに格納し、新しいチャットとともに画面に表示していくといった流れです。
そこで起こったのがこのような挙動です。
入力欄にチャットを入力し送信する

ページをリロード(F5/command+R)すると警告が表示されますが無視してリロード

すると入力欄には何も記述していないのに同じ内容のチャットが2つに増えてしまう

header関数でリダイレクト
これを阻止することができるのがheader関数です。header関数はレスポンスヘッダを設定することができる関数です。その中のLocationヘッダにURLを指定することでページをリダイレクトさせることができます。
if(isset($_POST['submit']) && $_POST['submit'] === "送信"){
// $_POSTから取得し
// データベースへ格納する処理
// POST処理の最後にリダイレクト処理
header("Location:./chat.php");
exit();
}
リダイレクト処理を記述することでレスポンスヘッダを変更できると言いました。では実際にどのようになっているのかデベロッパーツールで確認してみましょう。
デベロッパーツールはChromeに標準で装備されている開発者のためのデバッグツールです。ネットワークの通信やエラーの確認、そしてHTTP通信のリクエスト/レスポンスの内容を見ることができます。
リクエストにはサーバへの要求をまとめた情報、レスポンスにはサーバからの応答をまとめた情報が詰まっています。
そしてheader関数でレスポンスヘッダを設定することでリクエスト後の挙動を操作することができます。
LocationヘッダでURLを登録することでリクエストを送りレスポンスが帰ってきたタイミングでリダイレクトさせることができます。
ではデベロッパーツールでどのようになっているのか確認してみましょう!
デベロッパーツールでリダイレクトの挙動を確認
chat.phpをChromeで開いた状態で「F12」キーを押します。
「Elements/Console/Sources...」と並んでいるタブの中の「NetWork」タブをクリックします。1番左の●が赤色になっているか確認します。なっていなければクリックで赤色に変わります。ここでネットワークのログを表示するか選択できます。「Filter」の中の「Doc」をクリックします。

その状態で入力欄に値を打ち込みチャットを送信してみましょう。画面上は同じファイルにリダイレクトさせているので何も変化はありません。しかしヘッダ部分にはその履歴がしっかり残っています。
ファイル名(chat.php)が2つ表示されその横に「status」が表示されています。

このステータスは「HTTPステータス」と呼ばれるものでサーバでの処理結果を表しています。代表的なステータスを軽く紹介しておきます。
ステータス | 意味 |
---|---|
200 | 成功 |
302 | リダイレクト |
404 | リソースが見つからない |
500 | サーバーエラー |
今回でいうと1回目のchat.phpは「200/成功」を返し、2回目のchat.phpは「302/リダイレクト」を返しています。各ファイル名をクリックすることでその状態のヘッダ情報を確認することができます。
リダイレクト(302)した方のファイルをクリックしヘッダ情報を見てみましょう。
またタブが出てくるので「Header」をクリックします。その中に「General」/「Response Headers」/「Request Headers」が表示されています。
そのレスポンスヘッダーをクリック。すぐ横に「View parsed」と表示されていればそのまま「View Source」と表示されていればそこをクリックして「View parsed」に切り替えます。するとそこの「Location」のところに今回リダイレクトで指定したパス(相対パス)が記述されています。

POSTを送信したページ(200)から違うページ(302)に切り替えることでPOSTの中身を刷新しているという認識で良いのでしょうか。
ともかくこれでチャットを送信後何度リロードしても値が重複して格納されることは無くなりました。
リダイレクト後のページの表示とコツ
ページをリダイレクトさせるので例えばPOSTの処理部分に「echo」などで文字やチャット内容を表示してもページが変わるため表示されません。(実際には表示されているが表示されているのは1回目のchat.php)
if(isset($_POST['submit']) && $_POST['submit'] === "送信"){
// $_POSTから取得し
// データベースへ格納する処理
echo "ここのechoは1回目のchat.phpに表示される";
// つまり表示されない
// POST処理の最後にリダイレクト処理
header("Location:./chat.php");
exit();
}
なので「echo」などで何かHTMLに出力したい場合は記述する場所が大事になります。
記述すべき場所は「if分の外」です。if文では送信ボタンが押されたかどうかで分岐しています。送信ボタンが押された時(=POSTに値が格納された時)はリダイレクトさせ、送信ボタンが押されていない時(=POSTに値が格納されていない時)は表示したい内容を実行することができます。
if(isset($_POST['submit']) && $_POST['submit'] === "送信"){
// $_POSTから取得し
// データベースへ格納する処理
// POST処理の最後にリダイレクト処理
header("Location:./chat.php");
exit();
}
echo "if文の外であるここに記述する";
// データベースからチャットログを表示
この挙動がわかっていれば常に最新のチャットログを表示することができます。
通常アクセス時は現在のチャットログを表示(リダイレクトはなし)
チャット送信時はデータベースに値を格納(リダイレクトあり)
リダイレクト時は現在(更新ずみ)のチャットログを表示(リダイレクトなし)
POSTの二重送信はPOSTを扱う以上出てくる問題なので解決方法を覚えておいた方が良さそうですね!
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。