SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Remixを通じてWebを学ぶ

【検証】RemixとCloudflare Pagesを組み合わせるとどうなる?

Remixを通じてWebを学ぶ 第11回

  • X ポスト
  • このエントリーをはてなブックマークに追加

Cache-Controlのおさらい

 今回は、Remixからレスポンスを返す際に、Cache-Controlヘッダーを設定することで、CDNサーバーのキャッシュの挙動を制御してみます。まずは、Cache-Controlヘッダーについておさらいしておきましょう。Cache-Controlヘッダーは、Web標準の一環としてHTTPの仕様で定義されたヘッダーで、ブラウザやCDNサーバーとサーバーの間で、どのようにキャッシュを扱うべきかを指示するために使われます。

 指示の内容はディレクティブと呼ばれ、キャッシュを制御するために使われます。Cache-Controlヘッダーに指定できるディレクティブには、いくつかの種類があります。大雑把にジャンル分けすると、キャッシュの有効範囲を指定するディレクティブキャッシュの有効期限に関するディレクティブの2種類に分けられます。それぞれの挙動について、簡単に説明します。

キャッシュの有効範囲を指定するディレクティブ

 キャッシュの有効範囲を指定するディレクティブは、主にprivatepublicの2つがあります。これらのディレクティブは、キャッシュをどの範囲で利用するかを指定します(リスト1)。

[リスト1]publicとprivateの例
Cache-Control: private
Cache-Control: public

 privateを指定した場合、キャッシュはプライベートキャッシュ(多くの場合、ブラウザ内のローカルキャッシュ)にのみ保存されます。個人情報を含むコンテンツについてキャッシュしたい場合は、privateを指定する必要があります。

 publicを指定した場合、キャッシュは共有キャッシュに保存されます。共有キャッシュは、複数のユーザーで共有されるキャッシュのことで、CDNサーバーが持つキャッシュが該当します。公開情報のコンテンツについてキャッシュしたい場合は、publicを指定します。基本的には、ローカルキャッシュに保存したいか、共有キャッシュに保存したいかで、privateとpublicを使い分けます(図6)。

図6:ローカルキャッシュと共有キャッシュのイメージ
図6:ローカルキャッシュと共有キャッシュのイメージ

 有効範囲の枠組みでいうと、キャッシュを一切禁止するno-storeもありますね。

キャッシュの有効期限に関するディレクティブ

 キャッシュの有効期限に関するディレクティブは、もう少し多様です。基本的な考え方として、キャッシュには「新鮮である(fresh)」と「古くなっている(stale)」の2つの状態があるという考えのもとで設計されています。

 ディレクティブでは、新鮮で無くなるまでの時間を指定したり、新鮮でなくなった後の挙動を指定したりすることができます。キャッシュが古くなっていた場合、オリジンサーバーに問い合わせて、新しいコンテンツがないか確認する(=再検証する)ことが多いです。

表1:有効期限に関するディレクティブ
ディレクティブ 概要
max-age=N N秒後まではキャッシュが新鮮であるとみなす
s-maxage=N N秒後までは共有キャッシュが新鮮であるとみなす
stale-while-revalidate=N 古くなってからN秒後までの間は、レスポンスを再検証しつつ古いキャッシュを返す
stale-if-error=N オリジンサーバーがエラーを返した場合、N秒間は古いキャッシュを返す
must-revalidate キャッシュが古くなった場合、オリジンサーバーに再検証を要求する
proxy-revalidate 共有キャッシュが古くなった場合、オリジンサーバーに再検証を要求する
no-cache キャッシュを保存することはできるが、常にオリジンサーバーに再検証を要求する
immutable キャッシュが一度保存されたら、再検証を行わずにキャッシュを使う
no-transform キャッシュが保存される際に、コンテンツを変換しないようにする

 表1のimmutableについて、少し補足します。immutableは、キャッシュが新鮮な間の再検証を拒否するためのディレクティブです。実は、リクエストヘッダーのためのディレクティブがあり、代表的なものがブラウザのリロード操作時に付与されるCache-Control: max-age=0です。リクエスト時のmax-age=Nは「N秒以内に保存されたキャッシュがあれば再利用してよいが、なければ再検証してからレスポンスを返せ」という指示です。これをゼロ秒でリクエストすると、キャッシュが新鮮であっても再検証を要求することになります。

 immutableは、この再検証を拒否して、新鮮なキャッシュがあれば常に再利用します。時間経過で変化する可能性がないリソースに、immutableを指定することで、再検証のコストを削減できます。no-transformも補足しておきましょう。

 例えば画像データをレスポンスとして扱う場合、CDNや類似の機能を提供するサービスは、転送サイズを縮小するためなどの目的で、データを加工してから保存することがあります。

 no-transformは、そういった変換処理を行わないように要望するためのディレクティブです。画像データをそのままキャッシュに保存したい場合に指定します。これまでは有効期限を指定する挙動を中心に紹介していましたが、共有キャッシュが古くなった後の挙動を制御するディレクティブも設定できます。

 例えば、再検証を終えてからレスポンスを返す must-revalidateproxy-revalidateを使う場合は、リスト2のように指定します。

[リスト2]再検証を終えてからレスポンスを返す例
Cache-Control: public, s-maxage=3600, must-revalidate
Cache-Control: public, s-maxage=3600, proxy-revalidate # 共有キャッシュ専用の指定

 この場合、キャッシュを保存してから1時間経つと古くなったとみなされ、その後のリクエストに対しては、オリジンサーバーに再検証を要求します。再検証が成功した場合は、新しいコンテンツを返します。再検証が失敗した場合は、エラーになります。

 must-revalidateproxy-revalidateでは、再検証の間にユーザーを待たせてしまい、場合によってはユーザー体験を大きく損なうため、別の方法も用意されています。キャッシュが古くなっていた場合にも、一旦は古いキャッシュを返しつつ、オリジンサーバーへの再検証の要求も開始しておくstale-while-revalidateは、ユーザー体験とキャッシュの更新のバランスを取れるディレクティブです(リスト3)。

[リスト3]古いキャッシュを返しつつ、再検証を要求する例
Cache-Control: public, s-maxage=3600, stale-while-revalidate=7200

 この場合、キャッシュを保存してから1時間以内はキャッシュを返すだけの挙動になります。1時間を過ぎた場合は、さらに2時間の間だけ、古いキャッシュを返しつつ、オリジンサーバーに再検証を要求する挙動を行います。

 再検証が成功すると、キャッシュが新鮮な状態に戻ります。再検証が失敗した場合には、見た目上何も起こりません。

 キャッシュを保存してから3時間(1時間+2時間)経ってからのリクエストに対しては、must-revalidateと同じように、ユーザーを待たせてオリジンサーバーに再検証を要求します。ユーザーのアクセス頻度に応じて、秒数を調整しておくとよいでしょう。

今回のサンプルについて

 それでは、実際にRemixプロジェクトのキャッシュ設定を行ってみましょう。題材として、第3回で作ったHacker News Viewerを、第9回に解説したCloudflare Pages向け構成で再実装したものを利用します(図7)。

図7:Hacker News Viewerの見た目
図7:Hacker News Viewerの見た目

 ソースコード全体の解説は行いませんが、どんなアプリケーションなのかを要点だけおさらいしておきましょう。

  • ソーシャルニュースサイト「Hacker News」の人気記事20件を表示する
  • サイドメニューに人気記事20件のリンクを表示する
  • サイドメニューのリンクをクリックすると、その記事の内容とコメントを表示する

 こんなところでしょうか。今回は、各記事のURL(/top20/:記事ID)に対応した、SNSシェア用のサムネイル画像を生成するAPIをloaderで作成し、その画像ファイルをキャッシュしようと思います。

Remixプロジェクトの動的コンテンツのキャッシュ設定

 では、サンプルコードの現状の挙動を確認してみましょう。通信結果をChromeのDev Toolで表示し、Cf-Cache-Statusヘッダーの値を確認します。まずは、ページを初期表示するときにロードされる、HTMLファイルのキャッシュ状況を確認します(図8)。

図8:HTMLファイルのキャッシュ状況
図8:HTMLファイルのキャッシュ状況

 Cf-Cache-Statusヘッダーの値がDYNAMICになっていることがわかります。これは、キャッシュが行われない場合の挙動で、常にオリジンサーバーを呼び出して、サーバーサイドレンダリングを行っていることを示しています。

 Remixは初期表示の後の画面遷移からAjaxによるデータ取得を行うため、一度、サイドメニューの別の記事のタイトルをクリックして、画面遷移した時の通信内容を確認してみましょう(図9)。

図9:Ajaxによるデータ取得のキャッシュ状況
図9:Ajaxによるデータ取得のキャッシュ状況

 こちらもCf-Cache-Statusヘッダーの値がDYNAMICになっています。Remixの画面遷移に伴う通信は、デフォルトではキャッシュされないようです。Cloudflare公式ドキュメントのキャッシュのデフォルトの挙動を読んでみると、HTMLやJSONはキャッシュしないそうです。RemixのCloudflare Pagesでの挙動も、これに準拠しています。

次のページ
loaderにサムネイル画像を返すAPIを追加する

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Remixを通じてWebを学ぶ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/20101 2024/09/20 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング