CDN キャッシュ#
HTTP キャッシュと ETag で宣言した Cache-Control は、そのまま CDN(Cloudflare 等)のエッジキャッシュにも効きます。読み取り中心のサイトなら、エッジでヒットさせてオリジン(PHP / リモート DB)への到達をほぼゼロにできます。ただし HTML を CDN に載せるには CDN 側の設定が要る こと、そして Relayer 特有の「セッション Cookie で全 BYPASS」する罠があるので、その 2 点を中心にまとめます。
ブラウザと CDN を分ける(max-age と s-maxage)#
Cache の maxAge はブラウザ、sMaxAge は共有(CDN)キャッシュの鮮度です。 ブラウザキャッシュはパージできない(一度配ると修正が届かない)一方、 CDN はパージできるので、非対称に振るのが定石です。
// ブラウザは短く、CDN は長く。編集は CDN パージで即時反映できる
new Cache(public: true, maxAge: 300, sMaxAge: 2592000, etag: $etag);
// → Cache-Control: public, max-age=300, s-maxage=2592000共有ポリシーは 1 か所(例: App\PageCache::timed())に集約し、TTL をそこだけのノブにしておくと運用が楽です。
Cloudflare で HTML をキャッシュさせる#
Cloudflare は 既定では静的アセット(拡張子ベース)しかキャッシュせず、 HTML / JSON は「動的」扱いで素通りします。s-maxage を付けてもそれだけでは効きません。対象パスに Cache Rule(旧 Page Rule の "Cache Everything" 相当=Eligible for cache)を作り、Edge TTL を オリジン尊重(Use origin cache-control) にします。これで Cloudflare は s-maxage を採用し、エッジで指定秒数キャッシュします。
- 対象は読み取り系すべて(例:
/,/docs/*,/search,/api/*)。ルールのパス範囲が一部だけだと、その外のページは延々MISSのままキャッシュされません。 - デプロイのたびにゾーンをパージする(CI から Cloudflare API の
purge_everythingを叩く等)。エッジに残った古い HTML は前のデプロイのフォームやrosanna-defer.jsを配り続けるので、フォームの送信や defer の取得が新しいサーバと食い違います。
最大の罠: セッション Cookie で全 BYPASS#
Cloudflare は Set-Cookie を含むレスポンスを問答無用でキャッシュ回避 します(cf-cache-status: BYPASS)。ここで Relayer 特有の落とし穴があります。
Relayer のページ描画(Rosanna)はセッションも状態も使いません。 session_start() が走るのは、認証(Authenticator)やサーバアクションの CSRF トークン(_rosanna_csrf)など、ページがセッションに触れたときだけです。セッションに触れないページは Set-Cookie: PHPSESSID が出ないのでエッジキャッシュ可能です。触れるページだけが Set-Cookie を出し、 Cache-Control: private, no-store で返るので、その分は意図どおり BYPASS されます。
ポイント:
- アプリ側で
session.use_cookies=0を立てて塞がない。CDN は通っても、後で auth / CSRF を使うページを黙って壊します。直し方は「フレームワークに任せる」が正解。 - 別件で
session.cache_limiterは空にしておく。セッションが開始されると PHP がCache-Control: no-store, no-cache, must-revalidateを注入し、ページごとのキャッシュ方針を上書きしてしまうためです。
// public/index.php、Relayer::boot() より前
\ini_set('session.cache_limiter', '');効いているかの確認#
レスポンスヘッダを実測します。
curl -sI https://example.com/docs/some-page | \
grep -iE 'cache-control|cf-cache-status|age|set-cookie'set-cookie:が無いこと(あれば BYPASS の原因)。cf-cache-status:がMISS→ 再アクセスでHIT、age:が増えていく。cache-control:に意図したs-maxageが乗っていること。
DYNAMIC は Cache Rule 未設定(HTML 非対象)、BYPASS は Set-Cookie 等でキャッシュ除外、を意味します。ゾーンをパージした直後はエッジ未投入でしばらく MISS が続くので、数十秒おいて再確認してください。
編集の反映とパージ#
max-age / s-maxage の内側ではオリジンへ問い合わせないため、コンテンツを更新しても CDN は最大 s-maxage 秒だけ古いままになります。デプロイ時にゾーンをパージする CI を組んでいればデプロイ反映は即時ですが、デプロイを伴わずに内容だけ更新する運用(外部 DB を直接編集する等)では、その更新はパージするまで CDN に出ません。長い s-maxage を使うなら、内容更新の経路にも CDN パージを必ず組み込んでください。
改訂履歴 (4)
- v1.1.0: usePHP→Rosanna(rosanna-defer)に合わせて更新
- サイドバー再構成: 開発カテゴリ追加に伴う order 調整
- use-php 0.7.0/0.7.1・relayer ^0.12.1 の依存バージョン記述を削除し SessionStorage 遅延化を現在形で説明
- 新規作成