Relayer
Docs運用cdn-cache

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 の取得が新しいサーバと食い違います。

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 パージを必ず組み込んでください。

関連: HTTP キャッシュと ETag ・ デプロイ(Dockerfile)

最終更新 — 2026-11-04
改訂履歴 (4)
  • v1.1.0: usePHP→Rosanna(rosanna-defer)に合わせて更新
  • サイドバー再構成: 開発カテゴリ追加に伴う order 調整
  • use-php 0.7.0/0.7.1・relayer ^0.12.1 の依存バージョン記述を削除し SessionStorage 遅延化を現在形で説明
  • 新規作成