「全部POSTでよくない?」がモヤる人へ|HTTPメソッド使い分けの正論と現場のリアル

目次

「全部POSTでよくない?」って言われてモヤッとした話

API設計の話をしていると、毎回のように出てくる議論があります。

全部POSTでよくない?

RESTfulに書くべし派と、全部POSTで何が悪い派の対立。最近もエンジニア界隈のSNSで燃えていて、片方が「HTTPメソッドの意味を100回読み直せ」と言えば、もう片方は「ルーターでDELETEを弾く環境もある」と現実解を返す。傍から見るとなんの宗教戦争だよって感じですが、どちらにも一理あって、どちらか片方だけが正しいわけではないのが事実。

結論を先に言ってしまうと、こういうことです。

  • 原則は使い分け:HTTPメソッドはちゃんと意味を持って設計されているので、RESTを名乗るなら使い分けるべき
  • ただし現場の制約は無視できない:WAFやインフラ、フレームワークの都合で「全部POST」が現実解になるケースもある
  • 絶対にやっちゃダメなパターンはある:GETで更新処理、エラーでも200返す、みたいな反パターンは派閥に関係なくNG

本記事では、正論(RESTfulの原則)と現場のリアル(POSTオンリーが成立してしまう環境)を整理しつつ、「結局どう判断すればいいか?」の判断軸を提示します。「なんとなく使い分けてるけど、実は理屈が分かってない」という人にも、「全部POSTで何が悪いの?」と思っている人にも、両方に役立つ内容です。

まず原則:HTTPメソッドの本来の役割

議論に入る前に、HTTPメソッドそれぞれの役割をおさらいしておきます。「分かってるよ」という人も、意外と細かい部分で抜けてたりするので一緒に確認しましょう。

メソッド 主な用途 安全性 冪等性 具体例
GET 取得 GET /users/1
POST 作成・任意処理 × × POST /users
PUT 置換(全部置き換え) × PUT /users/1
PATCH 部分更新 × ×(実装による) PATCH /users/1
DELETE 削除 × DELETE /users/1

ここで出てくる「安全性」と「冪等性」が、HTTPメソッド使い分けの核心。次の章でしっかり見ていきます。

「冪等性」と「安全性」を理解すると全部分かる

HTTPメソッドの設計には、「冪等性(idempotency)」「安全性(safety)」という2つの重要な概念があります。これを理解すると、「なんでGETとPOSTで分ける必要があるの?」という疑問にスッキリ答えられます。

安全性:サーバーの状態を変えない

「安全」なメソッドとは、呼び出してもサーバーの状態を変えないメソッドのこと。GETがその代表で、何度叩いてもデータベースの中身は変わらない(はず)。

これが大事な理由は、「予測可能性」です。クローラーやプリフェッチがGETを大量に叩いても、副作用がないと信じられているから安心して動かせる。

冪等性:何度叩いても同じ結果

「冪等」なメソッドとは、同じリクエストを何度送っても、最終的なサーバーの状態が同じになるメソッドのこと。

[冪等な例:PUT /users/1]
1回目: name=Alice にする
2回目: name=Alice にする  ← 結果同じ
3回目: name=Alice にする  ← 結果同じ
→ 何度叩いても最終状態は name=Alice

[冪等じゃない例:POST /users]
1回目: 新しいユーザー作成(id=1)
2回目: 新しいユーザー作成(id=2)  ← 別ユーザーが増える
3回目: 新しいユーザー作成(id=3)  ← さらに増える
→ 叩くたびに状態が変わる

これが効いてくるのは、ネットワーク障害やリトライの場面。レスポンスが返ってこなかった時に「もう一度送っていいか」が冪等性で決まります。GETやPUT、DELETEは安心してリトライできるけど、POSTは二重登録の危険があるから慎重に扱う必要がある。

なぜこの2つが大事か

安全性と冪等性は、単なる教科書知識じゃありません。システム全体の信頼性に直結する設計の土台です。

  • キャッシュ:GETは安全だからCDNやブラウザがキャッシュできる
  • リトライ:冪等メソッドは失敗時に自動リトライしても安全
  • 並行処理:冪等なら順序を気にせず並列実行できる
  • 監視・ログ:メソッドで意図が分かるので、ログ追跡が簡単

つまりHTTPメソッドは、「このリクエストはこういう性質を持っていますよ」というコントラクト(契約)なんです。クライアント、サーバー、中間のプロキシ、CDN、すべてがこのコントラクトを前提に動いています。

絶対やっちゃダメな反パターン

「全部POSTでもいい派」と「ちゃんと使い分け派」の間で意見が分かれることは多いですが、派閥に関係なく絶対やっちゃダメなパターンがあります。これだけは守りましょう。

反パターン1:GETで更新処理

これは古くから言われている地雷。GETは安全(副作用なし)の前提で動いているので、ここで状態を変えると地獄が始まります。

❌ アンチパターン
GET /api/logout
GET /api/users/1/delete
GET /api/posts/1/like

なぜダメ?
→ ブラウザのプリフェッチで意図せず実行される
→ クローラーが踏んで意図せずデータが消える
→ 共有されたURLを開くだけで操作が走る
→ CSRF攻撃の的になる

過去には「ログアウトをGETで実装したサービスで、ブラウザの先読み機能が勝手にログアウトを叩いてユーザーが大量にログアウトされる」という事故もありました。GETでの更新処理は、ただ動かないだけじゃなく、セキュリティと運用の両方でリスクになるので絶対NG。

反パターン2:機密情報をGETで送る

パスワードやトークンをURLクエリに乗せるパターン。サーバーログ、ブラウザ履歴、リファラーヘッダーに残ってしまうので絶対避けましょう。

❌ 絶対NG
GET /api/login?email=user@example.com&password=secret123

✅ POSTでボディに入れる
POST /api/login
{ "email": "user@example.com", "password": "secret123" }

セキュリティ周りの基礎は DDoS攻撃とは?開発者が知るべきセキュリティの基本 なども併せて押さえておくと、API設計時のリスク感が研ぎ澄まされます。

反パターン3:エラーでも200を返す

これは「メソッド使い分け」とは少し違うけど、似た系統の罠。レスポンスボディに { "status": "error" } と書いて、HTTPステータスコードは200を返す設計。

❌ アンチパターン
HTTP/1.1 200 OK
{ "status": "error", "message": "ユーザーが見つかりません" }

✅ 正しいステータスコードを返す
HTTP/1.1 404 Not Found
{ "message": "ユーザーが見つかりません" }

これをやると、監視ツールがエラーを検知できないクライアント側のエラーハンドリングが複雑化する、といった問題が連鎖します。HTTPメソッドと同じく、ステータスコードも意図を伝えるコントラクトです。

「全部POST」が成立してしまう環境的理由

ここからが本記事の本題。「全部POSTでよくない?」と言いたくなる現場の事情があるのも事実です。正論だけ振りかざすのは雑なので、現実をちゃんと見ていきましょう。

理由1:WAFやインフラがGET/POST以外を弾く

セキュリティ強化目的で、GETとPOST以外のメソッドをデフォルトで弾くWAFが現実に存在します。社内ネットワークの古いプロキシや、特定の企業向けインフラだとよくある話。

「PUT/PATCH/DELETEを使いたいのに、リクエストがそもそも届かない」という事態に遭遇すると、技術的には正しくても物理的に動かない。この場合、現実解は3択です。

  • WAFの設定変更を交渉する(時間がかかる)
  • 全部POSTにする(手軽だが原則を捨てる)
  • X-HTTP-Method-Override ヘッダーで疑似メソッドを使う(折衷案)

理由2:X-HTTP-Method-Override という現実解

WAFやプロキシの制限を回避しつつ、API設計の意図を保つテクニック。POSTでリクエストを送るが、ヘッダーで「本来はPUTのつもり」と伝える方式です。

# 表面上はPOSTだが、サーバー側でPUTとして処理
POST /api/users/1
X-HTTP-Method-Override: PUT
Content-Type: application/json

{ "name": "Alice" }

サーバー側でこのヘッダーを見て、本来のメソッドにディスパッチする実装にしておけば、インフラ制約を回避しながら設計の意図を表現できる。Rails、Spring、Express系のいくつかのフレームワークが標準でサポートしています。

理由3:GraphQLは設計上「全部POST」

RESTじゃない選択肢として、GraphQLは仕様上ほぼ全リクエストがPOSTです。これは「使い分けがダメ」じゃなく、そもそもRESTとは違う思想で設計されているから

  • エンドポイントは /graphql 一本
  • 取得(query)も更新(mutation)も全てPOST
  • 意図は「メソッド」じゃなく「クエリ本文」で伝える

つまり、「全部POST」がダメなんじゃなく、「RESTを名乗りながら全部POST」がチグハグということ。GraphQLを採用しているなら胸を張って全部POSTでOKです。

理由4:GETの文字数制限とプライバシー懸念

GETにはURLの長さ制限があり、ブラウザやサーバーによって異なるが概ね2000〜8000文字程度。複雑な検索条件をJSONで渡したい時など、GETに収まらないからPOSTで取得するパターンも実務でよくあります。

また、機密性は高くないけど「ブラウザ履歴やログにパラメータを残したくない」という理由でPOSTにする判断もあり。これは「正論」より「実務上の安全側」を取った判断で、決して間違いではありません。

副作用のあるGETはどう設計する?問題

API設計でよく迷うのが、「データ取得しつつ、ついでに何か記録したい」というケース。たとえば「記事を取得しつつ、同時に閲覧履歴をログに記録する」みたいなやつ。

厳密に言うと、これはGETの安全性原則に反する。GETは副作用なしのはずなのに、閲覧履歴の更新という副作用が発生しているから。じゃあPOSTにするか?というと、それも変な気がする。

現実的な落とし所

このケースの判断軸を整理するとこう。

  • 副作用が「補助的」「監視・ログ的」:GETのまま、ログ記録は非同期で別処理にする → 一般的な選択
  • 副作用が「ユーザーから見て本質的」:POSTにする(例:投票、いいね、購入処理)
  • キャッシュされて欲しくない:GETでも Cache-Control: no-store ヘッダーを付ける

「閲覧履歴を残す」は補助的な副作用なので、GETのまま実装して、履歴記録はバックグラウンドで非同期に処理するのが現実的な落とし所。RESTの厳密性と運用の現実をバランスする典型例です。

「公開API」と「内部API」で違う判断軸

HTTPメソッドの使い分けは、そのAPIが誰に向けて公開されているかで判断軸が変わります。

API種別 使い分けの厳密さ 理由
公開API 厳密に守るべき 不特定多数の開発者が使うので、HTTPの慣例に沿わないと認知負荷が高い
パートナー向けAPI 原則使い分け、規約優先 契約相手の都合(WAF制約など)に合わせる必要がある
社内API チーム規約で統一 使う側も同じチームなので、規約さえあればPOSTオンリーもアリ
GraphQL/RPC 使い分けない そもそもREST的な思想じゃない

つまり、「使い分けるべきか?」の答えは状況次第。「公開APIなのに全部POST」はだいぶ筋が悪いけど、「社内APIで全部POSTで規約統一」は十分アリな判断です。

運用で詰みやすい設計の罠

使い分けに賛成派でも反対派でも、運用で詰みやすい罠はあります。設計時に気をつけるべきポイントを2つ。

罠1:同じパスでメソッド違いだけ→ログ追跡が地獄

RESTfulに振り切ると、こういう設計になりがち。

GET    /api/users/1
PUT    /api/users/1
PATCH  /api/users/1
DELETE /api/users/1

美しいけど、アクセスログを後から検索した時、メソッドカラムも見ないとどの操作か区別できないのがツラい。grepで /api/users/1 って検索すると全部の操作が混ざって出てくる。

運用観点を重視するなら、パス自体に動詞を含める方が追跡しやすいこともあります。

GET    /api/users/1
POST   /api/users/1/update
POST   /api/users/1/delete

RESTfulとしては中途半端ですが、運用ログ追跡や監査要件を重視するなら理にかなった選択。何を最適化したいかによって正解は変わります。

罠2:複合トランザクションが分割される

RESTでは1リソース1操作が基本ですが、現実には「残高を確認しつつ、資金拘束し、売買成立で残高更新」みたいな複合処理を1リクエストでやりたい場面があります。

これをRESTfulに分割すると、複数リクエストが必要になり、通信コストとトランザクション管理が複雑に。こういうケースは、1つのPOSTエンドポイントで複合処理を扱う設計の方が現実的なこともあります。

# 複合処理を1リクエストで
POST /api/transactions/execute
{
  "actions": [
    { "type": "reserve", "amount": 1000 },
    { "type": "trade", "items": [...] },
    { "type": "settle" }
  ]
}

これをRESTfulに分割すると、結局アプリケーション層でトランザクションをまたぐ複雑さを引き受けることに。使い分けの原則に固執して、本質的な要件を犠牲にしない判断も大事です。

RESTとRESTfulの違いを知っておこう

議論を整理する上で、「REST」と「RESTful」の使い分けも押さえておきましょう。実は別物です。

  • REST:Roy Fieldingが提唱した厳密なアーキテクチャスタイル。リソース指向、状態を持たない、HATEOAS、統一インターフェースなど6つの制約を満たす必要がある
  • RESTful:「REST的」というニュアンス。一般的にはHTTPメソッドの使い分け + リソース指向URL設計をしていれば「RESTful」と呼ばれることが多い

世の中のほとんどの「REST API」は厳密にはRESTじゃなくRESTfulです。なので、「RESTを名乗るなら使い分けろ」と言われるのは、実態としてはRESTfulの慣例のこと。HATEOASまで実装してる現代のAPIなんて、ほぼ皆無ですから。

つまり、「RESTfulじゃない設計」を「REST的じゃない」と批判するのは少しズレてるかもしれません。「自分のAPIをRESTfulだと名乗るなら、慣例には従いましょうね」というのが正確な表現です。

結局どう判断する?現場の落とし所

ここまで「正論」と「現場のリアル」の両方を見てきました。判断軸を整理するとこんな感じです。

Q1. このAPIを使うのは誰?
   公開API → ちゃんと使い分け(GET/POST/PUT/PATCH/DELETE)
   社内API → チーム規約で統一(POSTオンリーもアリ)
   GraphQL → 全部POSTで問題なし

Q2. インフラ制約はある?
   WAF/プロキシで弾かれる → X-HTTP-Method-Override or 全POST
   制約なし → 原則通り使い分け

Q3. RESTを名乗るか?
   名乗る → 慣例に従って使い分け必須
   名乗らない → 自由(ただし反パターンは避ける)

Q4. 反パターンに該当しない?
   GETで更新 → 絶対NG
   GETで機密情報 → 絶対NG
   エラーで200返す → 絶対NG

結局のところ、「ちゃんと考えて選んでいるか」が一番大事。「全部POSTで」も「ちゃんと使い分け」も、根拠があれば正解です。一番ダメなのは、「なんとなくPOSTだけ」「コピペでメソッド決めてる」みたいな思考停止

まとめ:原則を知った上で、現実に向き合う

本記事のポイントを整理します。

  • HTTPメソッドの原則:GETは取得・安全・冪等、POSTは作成・非冪等、PUT/DELETEは冪等など、それぞれが意味を持つ
  • 冪等性と安全性:キャッシュ・リトライ・並行処理の土台になる重要概念
  • 絶対NG:GETで更新、GETで機密情報、エラーで200返す
  • 「全部POST」が成立するケース:GraphQL、WAF制約、内部API、複合トランザクション
  • X-HTTP-Method-Override:制約環境での折衷解
  • RESTとRESTfulは別物:自分のAPIをどう名乗るかで責任が変わる
  • 判断軸:API公開範囲、インフラ制約、運用要件で決める

「全部POSTでよくない?」という問いに、白黒つけて答えるのは難しい。でも、原則を理解した上で「うちはこういう理由でこう設計します」と説明できる状態が、エンジニアとして一番強い。

派閥論争に巻き込まれるよりも、「メソッドが伝える意図と、現場の制約と、運用のしやすさを天秤にかける」習慣をつけましょう。それができれば、どの派閥でもない、現実を生きるエンジニアになれます。

API設計力をさらに磨く関連記事

HTTPメソッド使い分けの判断軸を押さえたら、次はAPI設計や実装力を体系的に強化していきましょう。設計判断の引き出しが増える関連記事を厳選しました。

API実装・サーバーレス開発を強化

セキュリティ・認証の判断軸を強化

設計判断の引き出しを増やす

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次