APIのSunsetヘッダー、それを送るべきタイミング
1分で読めます
Sunsetは、RFC 8594で定義された単一のレスポンスヘッダーであり、
あるリソースがいつ応答を停止するかを呼び出し元に伝える。APIの非推奨化は
告知・再通知・ブラウンアウト・廃止という一連のタイムラインと、それに伴う通知全体を扱っている。この
記事は、そのタイムラインの中でも唯一の機械可読な信号であるこのヘッダーについて、それが実際に何を
意味するのか、そしてRFC自身が送るべきではないと述べている唯一のケースについて扱う。
Sunsetヘッダーは何を伝え、何を伝えないのか
それは単一のHTTP日付、つまりそのリソースが応答しなくなると見込まれる時点を運ぶ:
Sunset: Sat, 31 Dec 2028 23:59:59 GMT
RFCはこれを保証ではなくヒントと呼んでいる。そのタイムスタンプまでリソースが動作し続けることを 約束するものではなく、その後どのような失敗になるかについても何も述べていない。呼び出し元は4xxを 受け取るかもしれないし、リダイレクトされるかもしれないし、あるいはまったく応答が返らないかも しれない。ヘッダーはそれらを区別しない。すでに過去になっているタイムスタンプは、値の誤りではなく 「今、あるいはいつでも」を意味する。これらはいずれもプロトコルによって強制されるものではない。 ヘッダーを一度も読まないクライアントは、常にそうしてきたのと全く同じように振る舞い、リソースが 消えたことを、結局はどのみち気づいていたのと同じ方法で知ることになる。
実際にいつ送るべきか
そのリソースが本当に応答を停止することになった時点でのみで、単に推奨されなくなっただけの段階
では送らない。RFCは非推奨化が二段階で起きることを明示しており、Sunsetヘッダーフィールドは
二段階目にのみ属する。最初の段階、つまりあるバージョンがもはや推奨されないという告知の段階
では、APIは完全に稼働し続けており、このヘッダーフィールドはそこには適用されない。バージョンが
実際に応答を停止する予定になった時点で初めて適用される。
これは非推奨化のタイムラインに直接対応している。Deprecationヘッダーは告知の段階、つまり
初日から送出される。Sunsetは古い挙動が実際に停止する日付を示し、それは四段階のタイムライン
が廃止と呼んでいるのと同じ日付だ。初日にSunsetを送ること自体は間違いではない。その時点で
すでに日付が確定しているならだが、非推奨化を告知してもいないのに送ったり、実際にはまだ廃止を
確約していないバージョンに設定したりすると、まだ決めてもいないことを呼び出し元に伝えてしまう
ことになる。
これはキャッシュと相互作用するか
しない。RFCはそれを直接述べている。SunsetとHTTPキャッシュは無関係な問題を解決するもので
あり、重なり合うのではなく補完し合うものとして読むべきだとされている。キャッシュ用のヘッダー
は、キャッシュされたコピーをいつ再利用して安全かを伝える。Sunsetはリソースの現在の状態に
ついては何も述べておらず、リソースそのものがいずれ存在しなくなるということだけを述べている。
レスポンスは、実際に終了を迎える瞬間の直前まで完全にキャッシュ可能でありうる。片方でもう片方
を近似しようとしてはならず、長いmax-ageが近づいてくる終了日を打ち消すとか、あるいはその逆
だとか考えてはならない。
一つのヘッダーで複数のエンドポイントを終了させることはできるか
ヘッダーはそれを返したリソースに適用されるが、RFCはサービスがより広い範囲を文書化することを 認めている。APIのホームリソースに設定された終了日を、そのURL一つだけでなくAPI全体が消える ことを意味すると定義することができる。ただし、これはあなたのスコープ規則をすでに知っている 呼び出し元に対してしか機能しないという落とし穴がある。ヘッダーを額面通りに読む呼び出し元には、 要求した一つのリソースについての終了だけが見え、それ以外は何も見えない。したがって、より広い 範囲は暗黙のうちにではなく、呼び出し元が見つけられるどこかに明記しておく必要がある。
このヘッダーと一緒に何を用意すべきか
廃止について説明されている場所へのリンクだ。RFC 8594はまさにこのために独自のsunsetリンク
リレーションを登録している。廃止方針や今後の日付、あるいは移行方法を説明するリソースを指し
示すためのもので、ヘッダーの単なるタイムスタンプとは別物だ。
HTTP/1.1 200 OK
Sunset: Sat, 31 Dec 2028 23:59:59 GMT
Link: <https://example.com/docs/sunset-policy>; rel="sunset"
そのリンクを自社のチェンジログの例や専用の移行ページに向ければ、
ほとんど誰のクライアントコードも検査しないヘッダーが、実際に探しに来た人間がすぐに見つけら
れるものに変わる。非推奨化のヘッダー
にあるsuccessor-versionリレーションと組み合わせれば、呼び出し元はレスポンスだけから、どこ
に行けばよいかと、これが何に置き換わるのかの両方を得られる。
これは最初から最後までどのように見えるか
v1が2027年3月1日に消えるとしよう。初日の非推奨化告知では、非推奨化のヘッダー
にしたがって、すべてのv1レスポンスにDeprecationとLink: rel="successor-version"を追加
するが、廃止日が仮の値ではなく本当に確定するまではSunsetを保留する。確定した後は、すべての
v1レスポンスが次を運ぶ:
HTTP/1.1 200 OK
Deprecation: @1756425600
Sunset: Mon, 01 Mar 2027 00:00:00 GMT
Link: <https://api.example.com/v2/reports>; rel="successor-version"
Link: <https://example.com/docs/sunset-policy>; rel="sunset"
呼び出し元のゲートウェイや監視は、どちらのヘッダーにも独立してアラートを設定できる。
Deprecationは新しいバージョンが存在することを、Sunsetはこのバージョンに時計が付いている
ことを示す。どちらのヘッダーも3月1日より前に変わる必要はない。変わるのはレスポンス自体で
あり、それはその日、そしてその前に予定されたブラウンアウトの期間中に起きる。
ブラウンアウトはヘッダーの内容を変えるか
予定されたブラウンアウトのためにヘッダーの値そのものを動かす必要はない。終了日はその前に
リソースが断続的に失敗していようがいまいが、変わらず終了日のままだ。変わるのはヘッダーでは
なくレスポンスだ。APIの非推奨化が説明しているように、告知された
日付の前の数週間に短い410 Goneの期間を設けておくことで、呼び出し元が失敗に初めて触れるの
が、ヘッダーの日付が到来する当日の本番ではなく、リハーサルになる。
FAQ
実際のHTTPクライアントやツールはSunsetヘッダーを本当に読んでいるのか?
クライアント側ではめったにない。その価値は主に、あなたと呼び出し元の間のインフラを運用して
いる人にとってのものだ。ヘッダーを監視するよう設定したAPIゲートウェイや監視ツールは、呼び
出し元のコードが気づくよりもずっと前に、自社やパートナーのチームにアラートを出せる。相手側
がすでに対応していると想定できる信号ではなく、自分でツールを組み立てる対象の信号として扱おう。
SunsetはCache-Control: max-ageと同じものか?
いいえ。max-ageはキャッシュされたコピーがどれだけ有効かについてのものであり、Sunsetは
リソースそのものがいつ存在しなくなるかについてのものだ。レスポンスは短いmax-ageと何年も
先のSunset日付を同時に持つこともできるし、その逆もありうる。どちらのヘッダーも互いを制約
しない。
エンドポイント全体ではなく、単一のフィールドが消える場合にSunsetを送ってよいか?
いいえ、このヘッダーはリソース、つまりURLに対してスコープされており、レスポンス本文の中の
フィールドに対してではない。エンドポイント自体は維持されたまま消えていくフィールドやパラ
メータ、あるいは列挙値については、代わりにDeprecationヘッダーとチェンジログの項目を使お
う。APIの非推奨化がまさにそのような変更の告知を扱っている。
終了日を動かす必要が生じたらどうするか? ヘッダーの値を更新し、最初にそれを告知したチェンジログの項目にもその旨を記そう。公開済みの 日付を黙って変更することは、呼び出し元に「あなたの日付は一つも本物ではない」と判断させる 原因になる。RFCがこの値をあえて保証ではなくヒントとして位置づけているのは、日付が時に動く ことがあるからだが、説明のない変更は次の日付への信頼も失わせる。
この記事の技術的な記述は第三者による確認を受けていません。誤りがあればお知らせください。修正します。