API 設計
『Web API: The Good Parts』を読んで理解した内容を、自分の言葉で書き直したもの。 本文の引用はしていない。HTTP メソッド・ステータスコード・ヘッダの仕様そのものは RFC に基づく事実。
このディレクトリ
- クイックリファレンス — 設計時に一目で見る短縮版
- チェックリスト — 公開前に潰す項目
- Web API 設計の要点 — 同じ主題を API Gateway での設定に落とした版
何を API 化すべきか
そのサービスができることを、すべて API 経由で行えるようにする。 EC サイトなら商品の検索と購入、写真共有サイトなら投稿とタグ付け。
より正確には、そのサービスが価値を生み出しているコアの部分をすべて公開する。 逆に、コアでない部分 — 家計簿サービスが外部から買っているデータでの通貨変換のような機能 — を単体の API として出すことにはあまり意味がない。それはそのサービス固有のものではないから。
エンドポイントの基本的な設計
URI
目指す状態は 「覚えやすく、どんな機能を持つ URI なのかがひと目で分かる」。 長い URI はたいてい、不要な情報が入っているか意味が重複している。
ホスト名は api.example.com
ホスト名の次はバージョン(v2 等)
バージョンは後方互換性を失う大きな変更を行うときだけ上げる
基本はすべて小文字を使う
標準化された単語以外は極力省略しない
複数のAPIを見比べて、最もよく使われている・最もしっくりくる単語を選ぶ
セキュリティの観点から、サーバー側が何で実装されているか分からないようにする
リソースは複数形にする
一意なリソースを表すのに必要な情報はパス、省略可能なパラメーターはクエリパラメーター
動詞は極力エンドポイントに入れない
パーセントエンコーディングされた文字を入れない
単語をつなげる必要がある場合はハイフンを使う
検索のためのエンドポイントには search を入れる
oauth2 認証用のパスは /oauth2/token 等
補足として押さえておくこと。
- 省略形は避ける。
svやuは読んでも何のことか確信が持てない。 ただし国コードのjpjpnのように ISO 3166 で標準化されてコード体系になっているものは別で、 むしろ独自表記より分かりやすい - サーバー側のアーキテクチャが透けないようにする。
/cgi-bin/get_user.php?user=100のような URI は、PHP で CGI として動いていることを教えてしまう - ルールを URI 全体で統一する。
一方は
/friends?id=100と複数形+クエリ、もう一方は/friend/100/messageと単数形+パス、 という混在をしない - 複数形にする理由は、DB のテーブル名と同じで
usersやfriendsが「集合」を表しているから - 単語の連結にハイフンを使うのは、ホスト名がハイフンを許可しアンダースコアを許可しないため。 URI 全体をホスト名と同じルールで統一するとハイフンに揃う
searchを入れるのは「これは検索用であって全件取得用ではない」と示すため- オープンな API と、提携先限定などクローズドな API は、エンドポイントを分けて公開する
バージョンを上げる基準
バージョンは頻繁に上げない。 複数バージョンを保守するのはコストが高く、利用者から見ても分かりにくい。
- 小さな変更は後方互換性を保って対応する(マイナーバージョン以下の変更で吸収する)
- 後方互換性を失ってよいと判断できるほどの大きな変更のときだけ上げる
だからメジャーバージョンで表すのが妥当ということになる。
HTTP メソッド
URI とメソッドの関係は、操作する対象と操作方法の関係。 URI の R は Resource で、リソースは何らかのデータを表す。 つまり URI が「何を」、HTTP メソッドが「どうするか」。
| メソッド名 | 説明 |
|---|---|
| GET | リソースの取得 |
| POST | リソースの新規登録 |
| PUT | 既存リソースの更新 |
| DELETE | リソースの削除 |
| PATCH | リソースの一部更新 |
| HEAD | リソースのメタ情報取得 |
- GET でサーバー上のリソースが変わることは基本的にない。 既読/未読や最終アクセス日時のように、参照されたこと自体を記録する場合は例外
- **POST は「その URI の配下に新しいデータを作る」**イメージ
- PUT は更新対象の URI そのものを指定して、内容を完全に上書きする
- HTTP の定義上は PUT で新規作成もできるが、 Web API では修正に PUT、新規作成に POST を使うのが一般的
メソッドが使えない場合の代替
HTML の Form は GET と POST しかサポートしていない。 クライアント側のライブラリが GET/POST にしか対応していないこともある。
その場合は POST を使い、本当は何のメソッドを使いたいのかをメタ情報としてサーバーに送る。 方法は2つ。
X-HTTP-Method-Overrideリクエストヘッダを使う_methodパラメータを使う。 Form のパラメータの1つとしてapplication/x-www-form-urlencodedで送る。Rails などが採用している
一覧に list は要らない
一覧のエンドポイントを設計していると list を付けたくなるが、なくても一覧だと意味は通じるし、
URI も短くなる。不要なら取り去ってよい。
クエリパラメーター
- 取得位置と件数は
offset/limit。 offset/limit の方が自由度が高く、利用者にとって使いやすい - 全文検索は
q。 Google の検索もクエリパラメータ名はq
認証
OAuth は、あるサービスに対して、ユーザーが別サービス(Facebook 等)に登録している情報の 利用を許可できる仕組み。
要点は、利用する側のサービスに Facebook のパスワードを入力しなくてよいこと。 認証に成功するとアクセストークンを受け取り、 認可された情報にだけアクセスできるようになる。
Resource Owner Password Credentials で認証する場合、
エンドポイントへは application/x-www-form-urlencoded、文字コードは UTF-8 で送る。
POST /v1/oauth2/token HTTP/1.1
Host: api.example.com
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=takaaki&password=abcde&scope=api
Authorization ヘッダはクライアント認証で、
アクセスしてきたサービスやアプリケーションが何であるかを特定するための情報。
- アプリを登録すると発行される ClientID と ClientSecret を、 ユーザー名/パスワードとみなして Basic 認証の形式で Base64 変換したものが入る
- 利用は任意だが、入れておくとアプリごとにアクセス数を制限したり、 許可していないアプリをブロックしたりできる
Authorizationヘッダではなく、client_idとclient_secretをリクエストボディに入れることもできるscopeはアクセスのスコープ指定(省略可能)
{
"access_token": "b77yz37w7kzy8v5fuga6zz93",
"token_type": "bearer",
"expires_in": 2629743,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}
token_typeのbearerは RFC 6750 で定義された OAuth 2.0 用のトークン形式。 以後はAuthorization: Bearer b77yz37w7kzy8v5fuga6zz93の形で送る- リフレッシュトークンは、アクセストークンを再発行してもらうための別のトークンで、 アクセストークンと同時に取得できる
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"error":"invalid_token"
}
再発行のリクエストは grant_type に refresh_token を指定して、リフレッシュトークンと一緒に送る。
POST /v1/oauth2/token HTTP/1.1
Host: api.example.com
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
レスポンス
ボディ
目安は 1スクリーン1 API コール、1セーブ1 API コール。
友人一覧が ID の配列だけを返すと、クライアントは ID ごとにもう一度問い合わせることになる。 複数ユーザーをまとめて取れる API があっても、最低2往復が必要になってしまう。
DB の各テーブルの中身をそのまま返すだけの API は、たいてい使い勝手が悪い。 テーブルに ID しか入っていないとしても、それをそのまま返せばよいわけではない。 ユースケースを考えて、利用者が最もシンプルに扱える形を設計する。
ユースケースに沿った形で、できるかぎり少ないアクセス回数ですむAPI設計を心がける
取得する項目を利用者が選択可能にする(fieldsパラメーター)
HTTPのレスポンスボディは実際のデータのみを返す
レスポンスデータをオブジェクトに統一する
レスポンスデータはなるべくフラットに。ただし階層構造の方が分かりやすい場合もある
レスポンスのキーはなるべく少ない単語数で表現する
複数の単語を連結する場合、連結方法はAPI全体を通して統一する(JSONではキャメルケース)
日時の形式はRFC3339にする
取得項目を選べるようにする
fields パラメータで項目を指定させる。省略された場合は全項目、
全部だと多すぎるなら最も利用頻度が高いと思われる組み合わせを返す。
項目名を直接指定させる以外に、量の異なるセットをあらかじめ用意して名前で選ばせる方法もある。 Amazon の Product Advertising API では「レスポンスグループ」と呼ばれている。
エンベロープを作らない
メタ情報は HTTP のレスポンスヘッダで表現できる。 そうすればボディは実データだけになって無駄がなくなるし、 ヘッダの書き方を全 API で共通にすればクライアント側の処理を抽象化しやすくなる。
階層構造
Google の JSON Style Guide も「なるべくフラットに、ただし階層構造の方が分かりやすい場合もある」 という含みのある書き方をしている。
階層にすべき典型は、送信者(sender)と受信者(receiver)のように同じ構造を持つ場合。
クライアントが両方を同じデータとして処理できるようになり、
毎回 sender receiver の接頭辞を付けるより JSON 自体も小さくなる。
トップレベルは必ずオブジェクト
トップレベルが配列の JSON は JSON インジェクションのリスクがある。 script 要素で他サービスの JSON を読み込ませて、中身を不正に入手する手口。
<script src="https://api.example.com/v1/users/me" type="application/javascript"></script>
仕組みはこう。
- ルートの
{}は JavaScript ではブロックとして解釈されるので、 中に JavaScript のコードがあることが期待される。結果、script 要素で読み込むと構文エラーになる - 配列はそれ単体で正しい JavaScript になってしまうので、問題なく読み込まれる
リクエストヘッダに認証情報を入れないと取得できない API ならこの問題は関係ないが、 常にオブジェクトを返す癖をつけておく方が安全。
ページングの情報
続きがあるかどうかを hasNext のような名前で結果に含める。
「次があるか」だけでなく、次ページの URI や必要なパラメータを返すパターンもある。
キー名の付け方
なるべく少ない単語数で表現する。
意味を正確に表そうとすると userRegistrationDateTime のように長くなりがちだが、長すぎる。
/usersというユーザー情報の API なら、先頭のuserはなくてよい- 時刻には
updatedAtのようにatを付ける例が多いので、registeredAtにするとさらに短くなる - ユーザーが登録した日時はデータが生成された日時とほぼ同じなので、
createdAtでも表せる
連結方法(userId / user_id / user-id)については議論が残るが、
JSON ではキャメルケースが良いとされる。ただしスネークケースの API も現実にはたくさんある。
値の形式
| 対象 | 方針 |
|---|---|
| 性別 | フィールド名を gender にしたなら、値は "male" "female" の文字列にする |
| 日時 | 広く一般に公開する API では RFC 3339。1995-12-17T03:24:00Z / 1995-12-17T03:24:00+09:00。UTC なら Z 表記も可 |
| 大きな整数 | JavaScript は数値をすべて IEEE 754 の64ビット浮動小数として扱うので誤差が出る |
Twitter の API は、id に加えて同じ値を文字列として入れた id_str を返すことで対処している。
{
"id": 266031293949698048,
"id_str": "266031293949698048"
}
ステータスコード
| ステータスコード | 意味 |
|---|---|
| 100番台 | 情報 |
| 200番台 | 成功 |
| 200 | OK |
| 201 | Created |
| 202 | Accepted |
| 204 | No Content |
| 300番台 | リダイレクト |
| 301 | Moved Permanently(メソッドの変更を許可) |
| 302 | Found(メソッドの変更を許可) |
| 304 | Not Modified |
| 307 | Temporary Redirect |
| 400番台 | クライアントエラー |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 405 | Method Not Allowed |
| 408 | Request Timeout |
| 409 | Conflict |
| 429 | Too Many Requests |
| 500番台 | サーバーエラー |
| 500 | Internal Server Error |
| 502 | Bad Gateway |
| 503 | Service Unavailable |
200番台はリクエストが成功したときにだけ返す。
エラーなのにボディにエラー情報を入れて 200 を返す実装があるが、これは誤り。 理由は、汎用の HTTP クライアントライブラリの多くがステータスコードで成否を判断しているから。 200 を返すと汎用のエラー分岐が使えなくなり、クライアント側の手間が増える。
ぴったり当てはまるコードがない場合は、200 400 500 のように 00 で終わるコードを使う。
用途ごとの使い分け。
| コード | 使う場面 |
|---|---|
| 201 | ユーザー登録、ToDo の追加、画像アップロードなど、サーバー側に新しいものができたとき |
| 202 | リクエストした処理が非同期で行われ、受け付けたが完了していないとき |
| 200 / 201 | PUT・PATCH は 200 と操作したデータを返す。POST は 201 |
| 204 | DELETE |
リダイレクト系。
- リダイレクト先の新しい URI は
Locationレスポンスヘッダに入る - 301 は恒久的な移動、302 は一時的な移動
- 304 は前回取得から更新されていないことを表す。ボディは空になる。 返るのは、クライアントがキャッシュを持っていてその情報を送ってきたときだけ
- 302 と 301 は POST から GET へのメソッド変更を許可するが、307 と 308 は許可しない
クライアントエラー系。
- 400 は「その他」。 他の 400 番台では表せないエラーに使う
- 401 は認証(Authentication)、403 は認可(Authorization)のエラー
- 409 はリソースの競合。 ID などのユニークキーを指定して登録する API で、既に同じ ID がある場合など
- 404 は、ユーザーが存在しないのか URI 自体が間違っているのか切り分けられない。 利用者の開発効率のために、詳しい情報を添えて返す
エラー情報
エラーはヘッダに入れたくなるが、現実に公開されている API はほとんどがボディに格納している。 クライアント側から見た処理のしやすさが理由と思われるので、ボディに入れる方針で問題ない。
Twitter はエラーを配列で返す。
{
"errors": [
{
"message":"Bad Authentication data",
"code":215
}
]
}
エラー時にボディが HTML になってしまう API がある。 500・503・404 で多く、存在しないエンドポイントへのアクセスや、 コードのバグで処理が止まった場合に起きる。これは避ける。
メンテナンスで止める必要がある場合は 503 で停止中であることを伝える。
503 Service Temporarily Unavailable
Retry-After: Mon, 2 Dec 2013 03:00:00 GMT
エラーは具体的かつ正確に返すのが原則だが、セキュリティ上あえて曖昧にしたい場合もある。 詳しい情報はデバッグには役立つので、開発環境では正確に、本番環境では曖昧に返すことで両立できる。
エラー情報はボディに格納する
エラー時のボディがHTMLにならないようにする
メンテナンス時は503とRetry-Afterを返す
本番では、セキュリティ上あえて情報を曖昧にする判断もある
キャッシュ
| ヘッダ | 使いどころ |
|---|---|
Expires | 「毎日何時」のように更新のタイミングが決まっているもの |
Cache-Control | 定期更新ではないが更新頻度がある程度限られているもの。リアルタイム性がそれほど重要でない場合や、負荷のためにアクセス頻度を下げてほしい場合 |
- 両方を指定した場合は、より新しい仕様である
Cache-Controlが優先される Expiresに過去の日付や不正な値を入れたときの挙動はブラウザによって違うので、Cache-Controlだけを使う方がよい
条件付きリクエストの流れ。
- クライアントが最終更新日時を
If-Modified-Sinceで送る - サーバーは送られてきた情報と現在の情報を比較する
- 変更がなければ 304、変更があれば 200 と変更後の内容を返す。 このとき新しい最終更新日時やエンティティタグも送る
更新とキャッシュについて一番理解しているのはサーバー側なので、
「どれくらいキャッシュすべきか」の情報はきちんと返す。
期間を返せない場合でも Last-Modified などの更新情報は発信しておくと、
クライアントが無駄なアクセスを減らせる。
Vary は、キャッシュを一意に特定するのに URI 以外のどのリクエストヘッダを使うかを指定する。
URI が同じでもリクエストヘッダによって内容が変わるケースがあるため必要になる。
キャッシュなしの場合はCache-Control: no-cache
If-Modified-Since: Tue, 01 Jul 2014 00:00:00 GMT
Railsの場合はif stale?(last_modified: @post.updated_at)
Content-Type
メディアタイプが正しく設定されていないとセキュリティ上の問題になる。
JSON を誤って text/html で配信した場合、XMLHttpRequest 経由なら問題なく取得も解析もできてしまう。
だがその URI を直接叩くと、ブラウザは Content-Type を見て HTML として表示する。
結果、内部に埋め込まれた JavaScript が実行される。
{"data":"<script>alert('xss');</script>"}
Content-Type: application/json; charset=utf-8
XSSが可能になるので、メディアタイプは正しく指定する
X-Content-Type-Options: nosniff で Content Sniffing を止める
逆に言えば、JSON にユーザーが送った情報(名前など)を入れている場合でも、
Content-Type に application/json が指定されていれば、
多くのブラウザでは直接そのデータにアクセスしても問題は起きない。
Content Sniffing は、データの内容からデータ形式を推定するブラウザの機能。
CORS
特定のサイトからのみ許可: Access-Control-Allow-Origin: https://www.example.com
すべて許可: Access-Control-Allow-Origin: *
クライアントは Origin: https://www.example.com をリクエストヘッダーに入れる
事前チェックは OPTIONS メソッドで行われる
Cookie や Authentication ヘッダで認証している場合は Access-Control-Allow-Credentials: true
XMLHttpRequest でのみ使用可能な API の場合は X-Requested-With: XMLHttpRequest
注意点が2つ。
- CORS で
X-Requested-Withを付けると、プリフライトリクエストが必要になる - 逆に言えば、
X-Requested-Withを要求することで XMLHttpRequest などヘッダを追加できる方法からのみアクセス可能な API にできる
OPTIONS /v1/users/12345 HTTP/1.1
Host: api.example.com
Accept: application/json
Origin: http://www.example.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: X-RequestId
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: X-RequestId
Access-Control-Max-Age: 864000
Content-Length: 0
Content-Type: text/plain
※ 例の X-RequestId のように独自ヘッダを新しく作るなら、X- は付けない
(独自ヘッダに X- を付けない)。
JSON のエスケープ
JSON の文字列中で許可されていない文字は、ダブルクォート・バックスラッシュ・コントロール文字だけで、 それ以外のすべての Unicode 文字は格納できる。
だからこそ明示的にエスケープする。
<と>をエスケープすれば SCRIPT 要素を完全に無効化できる。 JSON ではそのまま書くことも許可されているが、<と>に変換する\"'も\"'の16進エスケープにしておくと、 誤認識でトラブルになる可能性が下がる
{"data":"<script>alert('xss');<\/script>"\}
XSRF 対策
最も一般的なのは XSRF トークン。
正規のフォームに、そのサイトが発行したワンタイムトークン (少なくともセッションごとにユニークなトークン)を埋め込んでおき、 それがないアクセスは拒否する。
ヘッダー
この節は 2026-08-31 に OWASP Secure Headers Project を取得して更新した。 元のノートにあった推奨のうち2つが、現在では非推奨になっていたため。
| 目的 | ヘッダ |
|---|---|
| HTTPS の強制 | Strict-Transport-Security: max-age=63072000; includeSubDomains |
| 読み込み先の制限 | Content-Security-Policy: script-src 'self' |
| MIME 推測の禁止 | X-Content-Type-Options: nosniff |
| クリックジャッキング対策 | X-Frame-Options: deny |
| リファラの制御 | Referrer-Policy: no-referrer |
| クロスドメインポリシーの制限 | X-Permitted-Cross-Domain-Policies: none |
| ログアウト時のデータ削除 | Clear-Site-Data: "cache","cookies","storage" |
| キャッシュによる露出の防止 | Cache-Control: no-store, max-age=0 |
| DNS 先読みの停止 | X-DNS-Prefetch-Control: off |
| ブラウジング文脈の隔離 | Cross-Origin-Opener-Policy: same-origin |
| 資源の保護 | Cross-Origin-Resource-Policy: same-origin |
| 埋め込みの制限 | Cross-Origin-Embedder-Policy: require-corp |
Content-Security-Policy は、読み込んだ HTML 内の IMG・SCRIPT・LINK 要素などについて、
どこからの読み込みを許可するかを指定するヘッダ。
Permissions-Policy(旧 Feature-Policy)はまだ策定中。
-Report-Only 系があるものは、遮断せずに違反だけ報告させて試せる。
Content-Security-Policy-Report-Only など。
独自ヘッダに X- を付けない
新しく作るヘッダに X- を付けない(RFC 6648)。
新しいパラメータを作る者は、名前に
X-やそれに類する接頭辞を付けるべきではない。
理由が実際に起きたことに基づいている。 実験的なパラメータは、そのうち事実上の標準になる。 後から標準化されると同じものに2つの名前が並び、 古い実装が元の名前を使い続けるので、新しい実装は両方を支え続けることになる。
上の X-RateLimit-* から RateLimit への移行が、まさにこの形。
受け取る側の規則もある。
X- が付いているかどうかだけで、標準か非標準かを判断してはならない。
既にある X- ヘッダを使うな、という話ではない。
RFC は既存の非標準パラメータについては何も推奨していない。
だから X-Content-Type-Options や X-Frame-Options は、そのまま使う。
使ってはいけないもの
元のノートに載っていた推奨が、現在では反転している。
| ヘッダ | 現状 |
|---|---|
X-XSS-Protection | 非推奨。有効にすると、かえってクライアント側に問題を持ち込みうる。 0 にして無効化し、CSP で守る |
Public-Key-Pins(HPKP) | 非推奨。下記 |
Expect-CT | ほぼ役目を終えた。2018年5月以降の証明書は既定で SCT に対応している |
Feature-Policy | Permissions-Policy に置き換わった |
Pragma | HTTP/1.0 の遺物。Cache-Control を使う |
X-XSS-Protection: 1; mode=block は、長く「XSS 対策」として書かれてきた。
現在はそう扱わない。古い資料をそのまま写すと、この種の反転を取り込む。
SSL 証明書が偽造されていないかのチェック
証明書の内容のハッシュ値と有効期限を記録しておき、以降のアクセスでそのハッシュ値を使って 証明書が正しいものか判別する仕組み。
Public-Key-Pins: max-age=2592000;
pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=";
pin-sha256="LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
この方式は使わない。 主要ブラウザが対応をやめている。
やめた理由が仕組みそのものにある。 鍵を失ったり、誤った値を固定したりすると、自分のドメインに自分で到達できなくなる (HPKP Suicide)。攻撃者に固定値を仕込まれる形の脅迫も想定された。 間違えたときに自力で回復できない設計だった。
記録として残すが、新規に採用するものではない。
Set-Cookie とセキュリティ
| 属性 | 効果 |
|---|---|
Secure | そのクッキーは HTTPS の通信のときだけサーバーに送り返される |
HttpOnly | そのクッキーは HTTP の通信でのみ使われ、JavaScript からアクセスできない。XSS でセッション情報が読み出されるのを防げる |
SameSite と __Host- プレフィックスを含めた全体は
セッション管理にある。
アクセス数制限
この節は 2026-08-31 に IETF の RateLimit Header Fields を取得して更新した。
X-RateLimit-*の3本組は標準化の過程で2本に置き換わっている。
標準化されたヘッダ
2つある。役割が違う。
| ヘッダ | 何を伝えるか | 変化 |
|---|---|---|
RateLimit-Policy | どういう制限方針か(静的・準静的) | 応答をまたいで一定であるべき |
RateLimit | いまの残量(動的) | リクエストごとに変わりうる |
RateLimit-Policy: "burst";q=100;w=60,"daily";q=1000;w=86400
RateLimit: "default";r=50;t=30
RateLimit-Policy の項目は q(割り当て量)が必須で、
qu(単位。既定は requests)、w(窓の秒数)、pk(分割キー)が任意。
RateLimit は r(残量)が必須で、t(リセットまでの秒数)と pk が任意。
残量が正でも、次が通る保証にはならない。 仕様がそう明言している。
なぜ置き換わったか
古い3本組は名前が同じでも意味が揃っていなかった。
X-RateLimit-Reset が、ある実装では窓が切れるまでの秒数、
別の実装ではミリ秒や UNIX 時刻や日時を指していた。
同じ名前で違うものを返すので、クライアントが一律に解釈できない。
これは結合の度合いでいう意味の連鎖そのもので、 「複数の要素が、ある値の意味について合意していること」が成立していなかった。
状態コードとの関係
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
429 は例示であって必須ではない。 403 を使う実装もある。
サーバーは状態コードによらず RateLimit を返してよい。
両方返す場合、クライアント側では Retry-After が優先される。
サーバー側は、RateLimit の t が Retry-After と同じ時点を指すようにする。
実装
利用者やアプリケーションごとのアクセス回数を数えておく必要があり、 通常は Redis などの KVS に記録する。
同じ問題への別の入口が分散システムにある。 レートリミットは、過負荷を受け止める手段の1つ。
関連
- 設計
- キャッシュ — Memcached と Redis の比較
- DB と API の境界
- セキュリティ — 攻撃側から見た構図