DESIGN / CODE / INFRA / AI

解説

同じURLでも言語が違う?Varyでキャッシュの応答を分ける条件を伝えよう

Accept-Languageで内容が変わるローカルHTTP例を確認。VaryとCache-Controlの役割、既定の応答や304にも同じ条件を返す理由を整理。

同じURLでも言語が違う?Varyでキャッシュの応答を分ける条件を伝えよう
保存リストを見る →
この記事の道しるべ 4項目

同じURLで、日本語の利用者には「こんにちは」、英語の利用者には「Hello」を返すページがあるとする。URLだけを見て応答を再利用すると、違う言語を渡してしまうかもしれないよ。

こうしたときに、応答の選択へ使ったリクエストヘッダーをキャッシュへ伝えるのがVaryだよ。HTTPヘッダーの基本が分かる人向けに、言語が異なる二つの応答を確認しよう。

URL以外の選択条件を伝える

Accept-Languageは、利用者が希望する言語を伝えるリクエストヘッダー。応答側がそれに応じて内容を変えるなら、例えば次を返す。

Vary: Accept-Language

Varyに従うキャッシュは、応答の再利用を判断するときに指定されたヘッダーも照合する。単にURLが同じだからと、異なる条件へ同じ応答を渡すことを避けるための情報なんだ。

二つの応答をローカルだけで読む

Python 3.12の標準ライブラリだけで、127.0.0.1の空きポートに練習用サーバーを作る。外部へ公開せず、ファイルの配信や実データの処理も行わないよ。

from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread
from urllib.request import Request, urlopen

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path != '/greeting':
            self.send_error(404)
            return
        language = self.headers.get('Accept-Language', '')
        text = 'こんにちは' if language == 'ja' else 'Hello'
        body = text.encode('utf-8')
        self.send_response(200)
        self.send_header('Content-Type', 'text/plain; charset=utf-8')
        self.send_header('Vary', 'Accept-Language')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)
    def log_message(self, *args):
        pass

results = []
with HTTPServer(('127.0.0.1', 0), Handler) as server:
    worker = Thread(target=server.serve_forever, daemon=True)
    worker.start()
    try:
        url = f'http://127.0.0.1:{server.server_port}/greeting'
        for language in ['ja', 'en']:
            request = Request(url, headers={'Accept-Language': language})
            with urlopen(request) as response:
                results.append((language, response.headers['Vary'],
                                response.read().decode('utf-8')))
    finally:
        server.shutdown()
        worker.join()
for result in results:
    print(*result)

結果はja Accept-Language こんにちはと、en Accept-Language Helloの二行。二つともVaryが付き、本文が変わることを確認できる。これは応答の確認であり、ブラウザやCDNのキャッシュ動作を実測するコードではないよ。

言語の選択も、練習用にjaだけ日本語とする単純な条件。実際のAccept-Languageには優先度や複数の言語が入り得るため、この比較をそのまま本番の言語交渉へ使わないでね。

保存期間を決める指示とは別

Varyは、保存してよいかや何秒保存するかを直接決めるヘッダーではない。それはCache-Controlなどの条件と合わせて考える。Varyを付けたから必ずキャッシュされる、という意味ではないんだ。

また、個人ごとの内容を言語の違いだけで安全に共有できるわけではない。認証・Cookie・個人情報を含む応答なら、その保存方針や共有キャッシュの扱いを別に設計する必要がある。

既定の応答でも条件をそろえる

公式資料は、同じURLでは304 Not Modifiedや既定の応答も含め、同じVaryの値を使うよう案内している。日本語側にだけ付け、英語側では省略すると、条件を一貫して伝えられないよ。

2026年10月29日にMDNとPython公式資料を確認し、作例の二言語・Vary・UTF-8本文・サーバー終了を検証した。実CDNの設定変更は行っていないよ。保存と再検証、304とETagも参考になるね。

参考・出典

公開と変更の記録
  • :初回公開

日時は日本時間です。記載のある変更は、確認できた編集記録に基づきます。更新日の違いだけから変更内容を推測して載せることはありません。

更新・訂正の方針 →

みんなのコメント 0

感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。

最初のコメントをどうぞ。