【入門編】HTTP/1.1におけるPOSTメソッドの非冪等性と副作用 – HTTPプロトコル・通信規格実践ガイド

なぜ「同じボタン」を2回押すと大変なことになるのか?——HTTPの「POST」と「冪等性(べきとうせい)」の秘密

こんにちは!インフラエンジニアとして、日々パケットの「渋滞」や「迷子」と向き合っている筆者です。

皆さんはWebサイトを見ていて、「送信ボタンを連打しないでください」という警告を見かけたことはありませんか?あるいは、うっかり二重クリックしてしまい、注文が2件飛んでしまった経験がある方もいるかもしれません。

実はこれ、Web通信の根幹を支えるHTTPというルール(プロトコル)において、POSTメソッドが持つ「非冪等(ひべきとう)性」という性質が深く関わっています。

今回は、ネットワークの難解な定義を一旦横に置いて、私たちの身近な「郵便システム」に例えながら、なぜPOSTは「危なっかしい」のか、そしてどう制御すればいいのかを紐解いていきましょう。

—

1. そもそも「HTTPメソッド」って何?

Webブラウザ(皆さんのスマホやPC)が、Webサーバー(情報の保管場所)と会話するための「挨拶の仕方」だと思ってください。

  • GET(ゲット): 「この情報を見せて!」(例:Webページを開く)
  • POST(ポスト): 「この情報を送るから、登録・処理しておいて!」(例:お問い合わせ送信、注文確定)

ここで重要なのが、「GETは何度やっても安全だが、POSTはやりすぎると事故る」というルールです。

郵便で例えると…

  • GET: 「カタログを見せてください」と何度頼んでも、届くカタログは同じですよね。これが「冪等(べきとう)」な状態です。
  • POST: 「この手紙を届けてください」と10回頼んだら、10通の手紙が届いてしまいます。これが「非冪等(ひべきとう)」な状態です。

サーバー側からすれば、「同じ手紙が10通も届いたら、10回分お金を請求しちゃうよ!」ということが起きてしまうわけです。

—

2. なぜPOSTは「非冪等」なのか?

「冪等(べきとう)」という言葉、初見だと呪文みたいですよね。数学用語で、「何度計算しても結果が同じになる」という意味です。

HTTP/1.1の仕様では、POSTは「サーバー上の状態を変化させるもの」と定義されています。
サーバーは「あ、新しい注文が来た!」「新しいユーザーが登録された!」と、内部のデータベースを書き換えます。

この時、ネットワークの通信環境は完璧ではありません。たまにパケットが消えたり、サーバーの反応が遅れたりします。すると、ユーザーのブラウザは「あれ?返事がないな。もう一回送ろう!」と再送(リトライ)をしてしまいます。

サーバーは「あ、また新しい注文だ!」と勘違いして、2つ目の処理を始めてしまう。これが、Web開発における「二重投稿問題」の正体です。

—

3. どうやって防ぐ?——現場で使う「トークン」の魔法

では、どうやって「これは2回目の再送ですよ」とサーバーに教えればいいのでしょうか?

もっとも一般的な手法は、「注文番号(トークン)」をあらかじめ渡しておくことです。これを現場では「冪等性キー(Idempotency Key)」と呼んだりします。

具体的な流れ

1. 下準備: 注文画面を開くとき、サーバーが「今回限りの使い捨て番号(例:abc-123)」をブラウザに渡す。
2. 送信: ユーザーが「購入ボタン」を押す際、この「abc-123」を一緒にサーバーへ送る。
3. 照合: サーバーは「abc-123」を受け取ったら、データベースに「この番号で処理済みか?」を確認する。
4. 結果: もし2回目のリクエストが来ても、サーバーは「あ、この番号はもう処理したから無視しよう」と判断できる。

—

4. 実装のヒント(コード例)

Webアプリケーションでこの仕組みを作る際、サーバー側のコードは概念的にこのような動きをします。

疑似コード:サーバー側での二重投稿チェックのイメージ

def handle_order(request):
# クライアントから送られてきた一意なID(トークン)を取得
request_id = request.headers.get(“X-Idempotency-Key”)

# データベースにこのIDが既に存在するか確認
if db.exists(“processed_requests”, request_id):
# 既に処理済みなら、エラーではなく「成功した時の結果」をそのまま返す
return “注文は既に受け付けています(重複分は無視しました)”

# まだ処理していなければ、データベースに保存
db.save(“processed_requests”, request_id)
process_payment() # 決済処理など

return “注文が確定しました!”

—

まとめ:ネットワークは「信頼できない」前提で動く

インフラやネットワークの世界では、「通信は必ず失敗するし、必ず重複する」という前提で設計を考えるのがプロの流儀です。

  • GETは「何度でもやり直してOK(参照)」
  • POSTは「サーバーの状態を変える(更新・作成)」

この違いを意識するだけで、皆さんが作るアプリケーションやインフラ設計の強度が一段と上がります。

最初は難しく感じるかもしれませんが、まずは「手紙を10回送ったらどうなるかな?」と想像するだけで、ネットワークの挙動がぐっと身近に感じられるはずです。次回のブログでは、このHTTP通信がさらに高速化する「HTTP/2」の世界について深掘りしていきますね。

それでは、また次回の記事でお会いしましょう!

コメント

タイトルとURLをコピーしました