【入門編】 Varyヘッダーによるキャッシュキーの制御 – Web APIアーキテクチャ・データ連携実践ガイド

こんにちは!インフラアーキテクトの私です。日々、世界中を駆け巡るパケットの海を見つめていると、「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 ヘッダーを意識してみてくださいね。それではまた、次回の深淵なネットワークの世界でお会いしましょう!

コメント

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