一覧CL-15-3 · Cloudflareでの静的教材配信

静的サイトの更新とキャッシュ

配信元の現在版、キャッシュが保持する版、HTMLが参照するアセットをそれぞれ確認します。

説明

配信先のファイルを更新しても、利用者が保存済みの応答を使えば、すぐに新しい内容へ切り替わるとは限りません。HTMLを再検証し、変更したJavaScriptには別のURLを使う構成なら、HTMLとアセットの版を対応させやすくなります。

ローカルHTTPの配信元と、時刻を固定したクライアントキャッシュを使います。同じ/app.jsの上書きで旧版が残る場合と、app.v1.js/app.v2.jsを分ける場合を比較し、ETagによる304と配信元のロールバックも確認します。

配信元の現在版、キャッシュが保持する版、HTMLが参照するアセットをそれぞれ確認します。

1版1を取得する

Releasesは実HTTPサーバーを起動します。クライアント側の時計は0、JavaScriptの保存期限は60秒です。

from releases import ReleasesHTTP配信元と固定時計のキャッシュを読み込む release = Releases()独立した配信環境を作る release.fetch("/")HTMLを取得する release.fetch("/app.js")共通URLのJavaScriptを取得する

HTMLはapp.v1.jsを参照し、共通URL/app.jsの本文もbuild=v1です。初回なので両方HTTP 200で取得しています。

2同じURLでは保存済み応答を使う

60秒の期限内に同じJavaScriptを取得します。

release.fetch("/app.js")期限内のキャッシュを参照する

cache HITでbuild=v1です。配信元への要求を省いて、保持する本文を使っています。

3配信元だけを版2へ更新する

配信元の現在版をv2へ変え、同じURLを取得します。

release.publish("v2")配信元を版2へ切り替える release.fetch("/app.js")旧URLのキャッシュを読む

配信元はv2でも、キャッシュからv1が返ります。配置完了と、利用者が新しい本文を取得した時点は一致しません。

4HTMLから新しいアセットURLへ進む

HTMLは毎回再検証します。更新されたHTMLが指すapp.v2.jsを取得すると、旧URLとは別のキャッシュ項目になります。

release.fetch("/")更新後のHTMLを再取得する release.fetch("/app.v2.js")新しいアセットURLを取得する

HTMLも新しいURLの本文もHTTP 200でv2になります。URLを版ごとに変えることで、旧URLの期限を待たず新しい内容を取得できます。

5HTMLが同じなら304になる

変更のないHTMLへIf-None-Match付きで要求します。サーバーのETagが一致すると本文を再送しません。

release.fetch("/")ETagを使って変更の有無を確認する

HTTP 304で、保存していたHTMLの本文を使います。304自体に新しいHTML本文が入っているわけではありません。

6旧URLも期限後に再取得する

時計を60秒へ進め、/app.jsを取得します。

release.advance(60)保存期限まで時計を進める release.fetch("/app.js")期限後の共通URLを再取得する

期限切れ後は配信元へ要求し、HTTP 200でbuild=v2になります。新旧版が混在する期間はキャッシュの設定にも左右されます。

7HTMLを旧版へ戻す

配信元をv1へ戻します。版付きアセットを両方残しているため、旧HTMLの参照先も取得できます。

release.publish("v1")配信元のHTMLを旧版へ戻す release.fetch("/")旧版HTMLを取得する release.fetch("/app.v1.js")旧版の参照先を取得する

HTMLとapp.v1.jsがv1にそろいます。ロールバック時にも、HTMLが参照する旧アセットを保持する必要があります。

8配信先と保存済み応答は別に管理する

固定のDNS設定例を読み、同じURLのキャッシュを再度確認します。DNSは名前の到達先を決めますが、保持済みの本文を書き換える操作ではありません。

print配信先名を示す固定DNS記録を読む({"name": "learn.example.test", "type": "CNAME", "target": "course.pages.dev", "ttl": 300}) release.fetch("/app.js")共通URLに残る版を確認する

DNS設定例を読んだ後も/app.jsはキャッシュのv2です。配信元をv1へ戻すだけで全URLの保存済み応答が消えるわけではありません。

まとめ

配信元の切替だけでは期限内のキャッシュは変わりません。

HTMLと版付きアセットを対応させると、新しいURLで新しい内容を取得できます。

DNS、配信元の版、各URLのキャッシュは別々に確認します。

操作履歴と解説を振り返れます。

Python HTTP server / 固定時計のクライアントキャッシュ

1
from releases import Releases release = Releases() release.fetch("/") release.fetch("/app.js")
origin HTTP 200 / <script src="/app.v1.js"></script>
origin HTTP 200 /app.js build=v1
2
release.fetch("/app.js")
cache HIT /app.js build=v1
3
release.publish("v2") release.fetch("/app.js")
origin_release=v2
cache HIT /app.js build=v1
4
release.fetch("/") release.fetch("/app.v2.js")
origin HTTP 200 / <script src="/app.v2.js"></script>
origin HTTP 200 /app.v2.js build=v2
5
release.fetch("/")
origin HTTP 304 / <script src="/app.v2.js"></script>
6
release.advance(60) release.fetch("/app.js")
now=60
origin HTTP 200 /app.js build=v2
7
release.publish("v1") release.fetch("/") release.fetch("/app.v1.js")
origin_release=v1
origin HTTP 200 / <script src="/app.v1.js"></script>
origin HTTP 200 /app.v1.js build=v1
8
print({"name": "learn.example.test", "type": "CNAME", "target": "course.pages.dev", "ttl": 300}) release.fetch("/app.js")
{'name': 'learn.example.test', 'type': 'CNAME', 'target': 'course.pages.dev', 'ttl': 300}
cache HIT /app.js build=v2

演習完了