こんにちは!インフラアーキテクトの私です。日々、世界中を駆け巡るパケットの海を見つめていると、「Webの仕組みって、本当に人間社会の縮図だなあ」としみじみ感じます。
さて、皆さんはWeb APIを作ったり使ったりする中で、「あれ、さっきと違うデータが返ってきたはずなのに、なんだか古いデータ(キャッシュ)がそのまま返ってきちゃったぞ?」という謎の現象に遭遇したことはありませんか?
「同じURLにアクセスしているのに、人によって、あるいはブラウザの種類によって中身を変えたい!」
そんな時に登場するのが、今回主役に据える Vary というHTTPヘッダーです。
なんだか聞き慣れない英語で難しそうに聞こえますよね。でも大丈夫です。一歩ずつ、身近な例えを交えながら優しく紐解いていきましょう!
—
1. 郵便配達員さんと「宛先のひと工夫」で例えてみる
まずは、インターネットの世界を離れて、私たちの身近にある「手紙や荷物の配達」を想像してみてください。
あなたは、遠くに住むお友達に、手紙を送ろうとしています。
この時、お友達の家(URL)は1つだけですが、手紙の内容を「日本語が読める人には日本語で、英語が読める人には英語で」書き分けて送りたいとしますよね。
郵便配達員さん(Webの途中にあるキャッシュサーバーやCDN)は、毎日たくさんの荷物を預かっては、あちこちの家に配っています。
もし配達員さんが、「同じ宛先(URL)だから、前に配った荷物の残り(キャッシュ)をそのまま次の人にも渡しちゃえ!」と適当に判断してしまったらどうなるでしょう?
日本語しか読めないお友達のところに、英語の手紙が届いてしまい、「読めないよ!」と困ってしまいますよね。
ここで、差出人のあなたが荷物の伝票にこう書いたとします。
「この荷物は、受け取る人が『日本語』が読めるか『英語』が読めるかによって中身が違うから、そこをちゃんと確認してから配ってね!」
この「受け取る人の条件によって中身が変わるよ」と配達員さんに伝えるマジックカードこそが、今回学ぶ Vary ヘッダーなのです!
—
2. Webの世界における「Varyヘッダー」の正体
Webの技術(HTTPプロトコル)において、キャッシュはWebサイトを高速に表示するための強力な味方です。通常、CDN(CloudflareやFastlyなど)やブラウザは、次のようなルールでデータを保存(キャッシュ)します。
- ルール: 「URLが同じなら、中身も同じはずだよね!」
しかし、現代のWeb APIはとってもスマートです。同じ /api/user/profile という一つのURLであっても、
- クライアントが「スマホ」なのか「パソコン」なのか
- リクエストを送った人が「日本語」を好むのか「英語」を好むのか
- 「管理者」なのか「一般ユーザー」なのか
によって、返すべきデータをガラリと変えたい場面が多々あります。
ここで Vary ヘッダーの出番です。APIサーバーがレスポンス(返事)を返すときに、次のようなおまけの情報を一緒に添えてあげます。
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept-Language, User-Agent
この Vary: Accept-Language, User-Agent という指定は、キャッシュサーバー(配達員さん)に対してこう命令しているのと同じです。
> 「おい!このデータをキャッシュするときは、URLだけで判断するなよ!
> リクエストに含まれていた Accept-Language(言語設定)と User-Agent(端末の種類)の組み合わせを『キャッシュの引き出し(キー)』に含めて保存してくれよな!」
これによって、キャッシュサーバーは「お、日本語を希望するスマホ用のデータ」と「英語を希望するPC用のデータ」を別々の引き出しにきちんと整理して保存できるようになります。
—
3. 現場でよくあるトラブルと、Varyの正しい設定例
「なるほど、じゃあ何でもかんでも Vary に設定しちゃえば安心だね!」……と言いたいところですが、ここにインフラエンジニアの腕の見せ所(そして罠)があります。
もし、すべてのリクエストで変わるような情報(例えば、毎秒変わるタイムスタンプや、ユーザーごとの完全なセッションIDなど)を Vary に指定してしまうとどうなるでしょうか?
キャッシュサーバーの引き出しが、ユーザーの数やアクセスのたびに無限に増えてしまい、「どのデータもキャッシュされず、毎回サーバーに直接リクエストが飛ぶ(キャッシュ効率が最悪になる)」という悲惨な状態を引き起こします。これを専門用語で「キャッシュクラッシング」や「キャッシュヒット率の低下」と呼びます。
ですから、Vary に指定するのは「本当にレスポンスの見た目や内容が変わる必要最低限のヘッダー」に絞るのが鉄則です。
実装例:言語と圧縮方式による制御
例えば、PythonのWebフレームワーク(Flaskなど)や、NginxなどのWebサーバーでレスポンスを返す際を考えてみましょう。
よく使われる代表的なパターンとして、「ブラウザが対応している圧縮形式(Accept-Encoding)」と「言語設定(Accept-Language)」に応じたVaryの設定を見てみます。
from flask import Flask, make_response, request
app = Flask(__name__)
@app.route('/api/greeting')
def greeting():
# クライアントが要求している言語をチェック(日本語か英語か)
accept_lang = request.headers.get('Accept-Language', 'en')
if 'ja' in accept_lang:
message = {"message": "こんにちは!"}
else:
message = {"message": "Hello!"}
response = make_response(message)
# 【ここがポイント!】
# キャッシュサーバーに対して、Accept-Encoding(圧縮方法)と
# Accept-Language(言語)の組み合わせごとにキャッシュを分けるよう指示する
response.headers['Vary'] = 'Accept-Encoding, Accept-Language'
return response
このコードでは、サーバーから返るレスポンスに Vary: Accept-Encoding, Accept-Language というヘッダーを付与しています。
これにより、CDNやプロキシサーバーは以下のように賢くキャッシュを管理してくれます。
1. ユーザーA(日本語・gzip圧縮対応)がアクセス ➔ 日本語版のgzip圧縮データをキャッシュ。
2. ユーザーB(英語・gzip圧縮対応)がアクセス ➔ URLは同じでも、Accept-Language が違うため、新規に英語版のgzip圧縮データをキャッシュ。
3. ユーザーC(日本語・gzip圧縮対応)がアクセス ➔ 1のキャッシュがヒットして爆速で返却!
このように、適切な Vary を設定することで、「ユーザーへの適切なパーソナライズ(多言語対応など)」と「キャッシュによる高速化・サーバー負荷軽減」という、一見すると矛盾する2つの要件を美しく両立させることができるのです。
—
4. まとめ:美しいAPI設計は「キャッシュの裏側」まで見通すこと
今回は、Varyヘッダーを通じて「レスポンスのバリエーションをキャッシュキーに含める方法」を解説しました。
- URLが同じでも、中身が条件によって変わるなら
Varyを使おう。 - 郵便配達員(キャッシュサーバー)に「どこを見て仕分けすべきか」を教えてあげるイメージを持つ。
- 何でもかんでも
Varyに指定するとキャッシュ効率が落ちるので、本当に必要なヘッダー(Accept-EncodingやAccept-Languageなど)に絞る。
Web APIの設計というと、綺麗なURL(/api/v1/users など)をつけることばかりに目が行いがちですが、その裏側を流れるHTTPヘッダーの挙動までコントロールできてこそ、真に「美しいインフラと調和したAPI設計」と言えます。
ぜひ皆さんも、日々の開発やインフラ構築の中で Vary ヘッダーを意識してみてくださいね。それではまた、次回の深淵なネットワークの世界でお会いしましょう!
コメント