3秒でわかる
POST処理のあとリダイレクトを返し、表示はGETで行う手法。再読み込みや戻る操作による二重送信を防ぐための定番の流れです。
もう少し詳しく
どういうものか
Post-Redirect-Get(PRG)は、フォームの送信を受けたサーバーが、結果のHTMLを直接返さずに303や302のリダイレクトを返し、ブラウザに別のURLをGETさせる流れのことです。利用者から見れば送信後に完了画面が出るだけですが、ブラウザの履歴に残る最後の操作がPOSTではなくGETになる点が違います。
なぜ必要か
POSTの応答としてHTMLを返すと、その画面で再読み込みをしたときにブラウザが「フォームを再送信しますか」と尋ね、うっかり続行すると同じ注文が2件入ります。戻るボタンでも同じことが起きます。リダイレクトを挟んでおけば、再読み込みで走るのは無害なGETだけです。
具体例
利用者 サーバー
│ POST /orders │ 注文を保存する
├──────────────────→│
│ 303 See Other │ Location は /orders/1234
│←──────────────────┤
│ GET /orders/1234 │ 完了画面を組み立てる
├──────────────────→│
│ 200 OK │
│←──────────────────┤// Express の例
app.post("/orders", async (req, res) => {
const order = await createOrder(req.body);
res.redirect(303, `/orders/${order.id}`); // ここで HTML を返さない
});
app.get("/orders/:id", async (req, res) => {
res.render("order_done", { order: await findOrder(<a href="/glossary/req-params" class="text-primary font-medium underline underline-offset-2 hover:text-primary-dark">req.params</a>.id) });
});つまずきやすいところ
リダイレクト先で「登録が完了しました」のような一度きりのメッセージを出したい場合、リダイレクトによってリクエストが切り替わるため、変数をそのまま引き継げません。セッションに一時的に置いて次のリクエストで消すフラッシュメッセージの仕組みを使います。ステータスコードの選び方も要注意で、302を使うと一部の古いクライアントがメソッドをPOSTのまま維持します。POSTからGETへ確実に変えたいときは303を指定します。
似た用語との違い
冪等性の確保という点では似た目的ですが、多重送信対策にはトークンを1回限り有効にするやり方もあります。PRGはブラウザの再読み込みに対する対策で、二重クリックそのものは防げないため、実務では両方を併用します。