CloudFrontでブラウザやCDNにおけるキャッシュ期間をレスポンスヘッダで指定する

ウェブサイトを公開するときにはパフォーマンス向上のためにキャッシュ期間の設定をすることが一般的です。特にCDNを使う場合は「ブラウザにおけるキャッシュ期間」と「CDNにおけるキャッシュ期間」のそれぞれに対して「HTMLは短く」「CSSは長く」のようなファイルの特性に応じた設定をしたいことが多くあります。

本記事ではこのようなファイルの特性に応じたレスポンスヘッダの設定をAWSのCloudFront+S3の構成で行う方法を紹介します。具体的にはオリジン (S3) で静的に設定する方法と、Lambda@Edgeを使って動的に設定する方法の2通りがあります。

ちなみにセキュリティ面の強化として X-Content-Type-OptionsX-Frame-Options などを設定してXSS攻撃やクリックジャッキング攻撃を防ぎたいという場合、ここで紹介するLambda@EdgeやCloudFront Functionsを利用することも可能ですが、CloudFrontの機能として設定することも可能であるためそちらを利用する方がいいかもしれません。

やりたいこと

Webサイトのパフォーマンス向上やオリジンの保護という観点で、ファイルの特性などに応じてブラウザやCDNそれぞれにおけるキャッシュ期間を設定します。

詳しいことは他サイト様にお任せますが、例えばHTMLファイルは頻繁に更新されるからブラウザキャッシュはしないがエッジキャッシュは数分する、画像は滅多に更新しないのでブラウザとエッジともに1週間キャッシュする、といったようにファイルの特性に応じてキャッシュの戦略を変更することで表示の高速化をしつつ更新の反映が遅れないようにするなどの調整が可能です。

前提:CloudFrontにおけるキャッシュポリシー

CloudFrontではBehaviorとしてパスパターンごとにキャッシングのポリシーを設定できます。

参考: キャッシュポリシーについて

つまり、以下のイメージのように「CSSは1週間」「HTMLは1日」のような設定が可能です。

キャッシュポリシーを設定した例

この方法はAWSにマネージされた機能で大変お手軽なのですが、エッジでのキャッシュ期間の指定はできるものの、レスポンスヘッダを設定しているわけではないのでブラウザ上でのキャッシュ期間についてはコントロールできません

CloudFrontではオリジンからのレスポンスヘッダとして Cache-Control: s-maxage を設定することでもエッジでのキャッシュ期間が指定できます。もちろんそれに加えて Cache-Control: max-age を指定すれば、そのままブラウザに返されますのでブラウザでのキャッシュ期間も設定可能です。

ここではキャッシュ期間は基本的にすべてレスポンスヘッダでコントロールするという方針とするため、キャッシュポリシーでは Minimum TTL: 5, Maximum TTL: 31536000, Default TTL: 5 とします。

CloudFrontで設定しておくポリシー

ちなみにMinimumやDefaultが0ではないのは、リクエスト数が大量になった場合においてもオリジンへのリクエスト数が増え過ぎないようにするためです。後述するようなLambda@Edgeの実行数も抑えることができます。AWSがデフォルトで提供する “CachingOptimized” や “Amplify” もMinimum TTLは0ではなく、そのような意図があるのではないかと推測しています(参考: 管理キャッシュポリシーの使用)。

レスポンスヘッダの設定方法

CloudFormationを使う場合、オリジンからのレスポンスヘッダを指定することで各種キャッシュ期間の設定が可能です。

Cache-Controlの設定期間の指定対象指定しない場合の動作
max-ageブラウザブラウザ依存
s-maxageCDNエッジDefault TTL
ExpiresブラウザとCDNエッジブラウザはブラウザ依存、CDNエッジはDefault TTL

オリジンからのレスポンスヘッダで設定された項目や値とキャッシュポリシーでの設定値の関係については以下にある通りです。

正直わかりづらいですが、基本的にはレスポンスヘッダで設定された値がキャッシュポリシーの Minimum TTL と Maximum TTL の間であればそれが適用されます。ヘッダに指定がなければCDNエッジには Default TTL が適用され、ブラウザはブラウザ依存です。

以下ではキャッシュ期間をレスポンスヘッダで指定する方法をご紹介します。CloudFrontでレスポンスヘッダを指定するには、オリジン(S3など)で事前に設定しておく方法と、Lambda@Edgeを使って動的に設定する方法の2通りがあります。

オリジン(S3)で設定する方法

オリジンがS3の場合、レスポンスヘッダを指定するにはオブジェクトのメタデータに設定を行います。

メタデータの設定イメージ

Webマネジメントコンソールから設定可能ですが、すべてのコンテンツにそれぞれ手動で設定するのは非現実的です。例えばAWS CLIを使って設定する場合であれば、cpsync で配置するときに --cache-control オプションで設定内容を指定します。(ちなみに --metadata オプションで指定すると Cache-Control ヘッダではなくユーザー定義のヘッダとして x-amz-meta-cache-control の設定となってしまいますので注意が必要です。)

# AWS CLIでの設定イメージ
aws s3 cp index.html s3://origin-bucket-example/ \
  --cache-control "public, max-age=0, s-maxage=3600" \
  --content-type "text/html"

サイトのデプロイのタイミングでブラウザでのキャッシュ期間を max-age に、CDNでのキャッシュ期間を s-maxage に設定することで、個々のファイルに対してそれぞれの設定が可能です。

この方法は静的に設定をしておけるので無駄が無くコードを書く必要がないのでバグも起きづらそうですが、 Cache-Control の内容ごとに別々でアップロードを行う必要があり、ファイルごとに更新タイミングのズレが比較的大きくなる恐れがあります。

また、ディレクトリ単位や拡張子単位などでまとめて設定するには --exclude オプションや --include オプションを利用する必要がありますが、これらのオプションは正規表現のようにシンプルで柔軟な指定はできないため、ある程度細かく指定したい場合には後述するLambda@Edgeを用いた方法がおすすめです。

Lambda@Edgeで設定する方法

CloudFrontではCloudFront FunctionおよびLambda@Edgeを用いて、リクエストに対して処理を挟むことが可能です。関数を呼ぶトリガーとして以下の4つがあります。

  • ビューワーリクエスト (Viewer Request)
  • オリジンリクエスト (Origin Request)
  • オリジンレスポンス (Origin Response)
  • ビューワーレスポンス (Viewer Response)

参考: Lambda@Edge 関数をトリガーできる CloudFront イベント

今回はキャッシュの期間指定をCloudFrontに伝える必要があるため、オリジンからCoudFrontへレスポンスを返す部分である「オリジンレスポンス」に設定を行います。

以下のような関数をLambda@Edgeとして作成し、CloudFrontのBehaviorのオリジンレスポンスに紐づけます。詳細なロジックについては自由にカスタマイズしてください。

'use strict';
exports.handler = (event, context, callback) => {
  const response = event.Records[0].cf.response;
  const request = event.Records[0].cf.request;

  // request uri
  const uri = request.uri;

  // response headers
  const headers = response.headers;

  // set cache-control
  if (response.status === "200") {
    if (uri.endsWith('.html')) {
      // html is not cached in browser
      headers['cache-control'] = [{
        key: 'Cache-Control',
        value: 'public, max-age=0, s-maxage=2592000'
      }];
    } else if (uri.endsWith('.webp') || uri.endsWith('.png') || uri.endsWith('.ico')) {
      // images is cached long time
      headers['cache-control'] = [{
        key: 'Cache-Control',
        value: 'public, max-age=604800, s-maxage=2592000'
      }];
    } else {
      // default
      headers['cache-control'] = [{
        key: 'Cache-Control',
        value: 'public, max-age=3600, s-maxage=2592000'
      }];
    }
  }

  callback(null, response);
};

関数で扱えるリクエストやレスポンスのデータは以下を参考にしてください。

まとめ

CloudFront (+S3) において、レスポンスヘッダに Cache-Control を指定してブラウザやCDNにおけるキャッシュ期間を設定する方法を紹介しました。

CloudFrontでは Cache-Control: s-maxage でCDNキャッシュ期間を指定可能であるため、オリジンレスポンスに紐づくLambda@EdgeやオリジンであるS3オブジェクトへの設定などで指定が可能です。各自の都合でこれらを使い分けていただければと思います。

最終更新 2024-03-25

広告

本記事はお役に立てたでしょうか。本ブログでは匿名でのコメントや少額から(15円~)の寄付などを受け付けております。もしお役に立てたのであればご支援いただけると大変励みになります。

Built with Hugo
テーマ StackJimmy によって設計されています。