ウェブサイトを公開するときにはパフォーマンス向上のためにキャッシュ期間の設定をすることが一般的です。特にCDNを使う場合は「ブラウザにおけるキャッシュ期間」と「CDNにおけるキャッシュ期間」のそれぞれに対して「HTMLは短く」「CSSは長く」のようなファイルの特性に応じた設定をしたいことが多くあります。
本記事ではこのようなファイルの特性に応じたレスポンスヘッダの設定をAWSのCloudFront+S3の構成で行う方法を紹介します。具体的にはオリジン (S3) で静的に設定する方法と、Lambda@Edgeを使って動的に設定する方法の2通りがあります。
ちなみにセキュリティ面の強化として X-Content-Type-Options
やX-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
とします。
ちなみにMinimumやDefaultが0ではないのは、リクエスト数が大量になった場合においてもオリジンへのリクエスト数が増え過ぎないようにするためです。後述するようなLambda@Edgeの実行数も抑えることができます。AWSがデフォルトで提供する “CachingOptimized” や “Amplify” もMinimum TTLは0ではなく、そのような意図があるのではないかと推測しています(参考: 管理キャッシュポリシーの使用)。
レスポンスヘッダの設定方法
CloudFormationを使う場合、オリジンからのレスポンスヘッダを指定することで各種キャッシュ期間の設定が可能です。
Cache-Controlの設定 | 期間の指定対象 | 指定しない場合の動作 |
---|---|---|
max-age | ブラウザ | ブラウザ依存 |
s-maxage | CDNエッジ | Default TTL |
Expires | ブラウザとCDNエッジ | ブラウザはブラウザ依存、CDNエッジはDefault TTL |
オリジンからのレスポンスヘッダで設定された項目や値とキャッシュポリシーでの設定値の関係については以下にある通りです。
正直わかりづらいですが、基本的にはレスポンスヘッダで設定された値がキャッシュポリシーの Minimum TTL と Maximum TTL の間であればそれが適用されます。ヘッダに指定がなければCDNエッジには Default TTL が適用され、ブラウザはブラウザ依存です。
以下ではキャッシュ期間をレスポンスヘッダで指定する方法をご紹介します。CloudFrontでレスポンスヘッダを指定するには、オリジン(S3など)で事前に設定しておく方法と、Lambda@Edgeを使って動的に設定する方法の2通りがあります。
オリジン(S3)で設定する方法
オリジンがS3の場合、レスポンスヘッダを指定するにはオブジェクトのメタデータに設定を行います。
Webマネジメントコンソールから設定可能ですが、すべてのコンテンツにそれぞれ手動で設定するのは非現実的です。例えばAWS CLIを使って設定する場合であれば、cp
や sync
で配置するときに --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オブジェクトへの設定などで指定が可能です。各自の都合でこれらを使い分けていただければと思います。