Back to posts

Cloudflareのキャッシュと静的サイト:何が速くなるのか

「Cloudflareを入れると速くなる」という話はよく見ますが、このサイトのように Cloudflare Pages で配信している静的ブログでは事情が少し違います。後ろに動的にページを生成するサーバーがいないので、「キャッシュが効けばオリジンが楽になる」という図式がそのままは当てはまらないからです。

このサイトでキャッシュについて気にするのは、実のところ2つだけです。新しいデプロイがいつ全員に届くか。そして、HTML と静的アセット(CSS や JS、画像)で扱いがどう違うか。この2点を押さえておけば、「更新したのに古いページが見える」「計測するたびに数字が違う」といった混乱のほとんどは説明がつきます。

エッジキャッシュの仕組みをざっくりと

Cloudflare のキャッシュは、世界各地のデータセンター(エッジ)にコンテンツのコピーを置く仕組みです。あるファイルへの最初のリクエストでは、エッジにまだコピーがないので、取りに行ってから返します。レスポンスヘッダーでは cf-cache-status: MISS と出る状態です。そのコピーが残っていれば、2回目以降に同じデータセンターへ来たリクエストはコピーから返ります(HIT)。往復が短くなる分、ここが速くなります。

Pages の場合、アップロードしたアセットは自動で Tiered Cache(階層型のキャッシュ)から配信される決まりになっていて、自分で有効化する作業はありません。設定項目が少ない代わりに、「何がキャッシュの対象になるか」というデフォルトの挙動を理解しておくことが大事になります。

HTMLと静的アセットで扱いが違う

まず、通常の Cloudflare CDN のデフォルトを押さえます。キャッシュ対象になるかどうかは、MIME タイプではなくファイルの拡張子で決まります。対象になるのは CSS、JS、画像、フォントといった静的ファイルで、HTML と JSON はデフォルトではキャッシュされません。動的サイトで「HTML がキャッシュされず、毎回サーバーまで取りに行く」のはこのためです。

一方の Pages は、配信の仕組み自体を Pages 側が持っていて、考え方が少し違います。公式ドキュメントの説明では、Pages にデプロイしたアセットは次のデプロイまで CDN にキャッシュされ続けます。違いを表にまとめると、こうなります。

観点HTML静的アセット(CSS / JS / 画像 / フォント)
通常の CDN キャッシュの対象かデフォルトでは対象外(拡張子で判定され、HTML は含まれない)拡張子で判定され、デフォルトで対象になる
このサイト(Pages)での更新デプロイでファイルごと差し替わるデプロイで差し替わる。ファイル名にハッシュが付くものは、内容が変われば別 URL になる
古いコピーが残るリスクカスタムドメイン側に独自のキャッシュ設定を足した場合に起こりうる同じファイル名のまま中身だけ差し替えると起こりうる。ハッシュ付きなら実質起きない
ブラウザ側の扱いEtag で再検証され、変更がなければ 304 で済む同じく Etag で再検証される

ブラウザ側の扱いについて補足します。Pages は、キャッシュ可能なアセットに Cache-Control: public, max-age=0, must-revalidate を付けることがあります。一見「キャッシュしない」指定に見えますが、これは「使う前に確認せよ」という意味です。ブラウザは Etag を添えて確認に行き、内容が変わっていなければ 304 Not Modified が返って、転送量は小さく済みます。ブラウザに長く保存させるのではなく、確認を安く済ませる設計、というわけです。

更新が届く仕組み:デプロイとハッシュ

このサイトは Astro でビルドしていて、JS や CSS はファイル名に内容のハッシュを含む形で出力されます。中身が変わればファイル名も変わるので、読者のブラウザやエッジが古いファイルを掴み続ける問題は、構造的に起きません。新旧どちらの HTML も、自分と同世代のアセットを参照するからです。画像についても、Astroの画像最適化を標準の astro:assets に寄せる で整理したとおり、最適化の対象にした画像はハッシュ付きで出力されるので、同じ仕組みの上に乗ります。

なお Pages 側の仕様として、デプロイしたアセットはデータセンターごとにキャッシュされ、保持期間(TTL)は1週間とされています。新しいデプロイのあとも、旧アセットが最大で1週間残ることがあります。ただしこれは不具合ではなく、むしろ安全側の挙動です。キャッシュに残っている旧ページから旧アセットへの参照が、しばらくの間は切れずに済みます。

ポイント: 「更新が反映されない」と感じたとき、原因は大抵この1週間の保持ではありません。先に疑うべきは、自分で足したキャッシュ設定と、自分のブラウザのキャッシュです。

そもそも更新の確認は本番に出す前にプレビュー側で済ませる運用にしていて、その流れは Cloudflare Pagesのプレビューデプロイ運用メモ にまとめました。本番の配信で起きる話と、プレビューでの確認の話を分けておくと、「反映されない」の切り分けがだいぶ楽になります。

Cache Rulesは触る? 触らない?

Cache Rules は、URL のパターンごとに「キャッシュ対象にするかどうか」「エッジに何秒置くか(Edge TTL)」「ブラウザに何秒置かせるか(Browser TTL)」などを決める仕組みです。独自サーバーを Cloudflare の後ろに置くサイトでは調整の主戦場ですが、Pages の静的ブログでは出番が限られます。

触らなくてよい場面は、このサイトの通常運用のほぼ全部です。Pages のデフォルトは「できるだけキャッシュしつつ、最新の内容を渡す」バランスに調整されていて、公式ドキュメントも、ほとんどの場合は独自のキャッシュ設定を避けるよう案内しています。

注意: カスタムドメイン側で独自にキャッシュを足すと、デプロイ後も古いアセットが配信されたり、Pages のリダイレクトや Functions の処理より先にキャッシュが返って動作がおかしくなったりすることがある、と公式ドキュメントは注意しています。

触る場面として公式ドキュメントが挙げているのは、内容ハッシュ入りのファイル名を持つ CSS や JS のような「変わらないことが保証できるアセット」について、独自ドメイン側でキャッシュを強めるケースです。このサイトでは現状そこまでやっていません。遅さに困っていない場所へ、先回りでルールを足さない、という判断です。

パージ(キャッシュ削除)の出番

キャッシュのパージは、エッジに残っているコピーを消す操作で、URL 単位、パス(プレフィックス)単位、ホスト単位、全部消す、といった指定ができます。ただ、Pages の静的ブログでパージが本当に必要になる場面は多くありません。更新はデプロイが面倒を見るからです。

出番があるとすれば、「同じ URL のまま中身を差し替えた」ケースです。たとえばファイル名にハッシュが付かない画像を同名で差し替えた場合、エッジやブラウザに古いコピーが残っている間は、見る人によって絵が違う状態になります。あとは、カスタムドメイン側に独自設定を入れてしまい、古いコピーが残ったときの後始末です。

そして前提として大事なのは、パージで消せるのはエッジのコピーだけだということです。すでに読者のブラウザに保存されたコピーは、サーバー側の操作では消せません。ここを取り違えると、「パージしたのに直らない」という無駄な往復が起きます。

計測するときはキャッシュの状態も一緒に見る

最後に、速度を計測するときの注意です。同じページでも、エッジにコピーがない初回(MISS)と、コピーから返る2回目(HIT)では所要時間の出方が変わります。さらに、計測がどのデータセンターに当たるかでも結果はぶれます。手元のブラウザで開いた場合と計測サービスの拠点から開いた場合で、数字が違うのは当たり前、ということです。

なので、1回の計測値で判断しないこと。見るなら、レスポンスヘッダーの cf-cache-status のように「その計測がキャッシュのどの状態だったか」とセットで見ます。キャッシュが効いた状態の数字だけを見て「速くなった」と結論づけるのは、このサイトのような構成では特に危ういと思っています。

まとめると、Pages の静的ブログでキャッシュについてやることは、設定を盛ることではなく、デフォルトの挙動を理解して邪魔をしないことです。更新の伝わり方は、デプロイとファイル名のハッシュが保証してくれる。Cache Rules やパージは、「同名で差し替えた」「独自設定で古いコピーが残った」という具体的な理由があるときだけ出す道具、という位置づけにしています。

参考