Amazon CloudFrontのキャッシュ期間をコントロールする(2015年6月版)

2015.06.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
CDN(Contents Delivery Network)サービスのCloudFront、皆さん使っていますか?完全従量課金で初期費用ゼロのスケーラブルなCDNとして様々な場面で活用できると思います。CDNの運用で問題になりがちなのが、キャッシュにまつわるトラブルではないでしょうか。今回は、CloudFrontで提供するコンテンツのキャッシュ期間をコントロールする手法をご紹介します。

CDNによるコンテンツ配信のキャッシュは2種類

CloudFrontは世界中に点在するエッジロケーションと呼ばれるデータセンターのキャッシュサーバーを経由してWebコンテンツを配信する仕組みです。ただ経由させるだけではリクエストアクセスが配信元(オリジン)に集中してしまうため、一度配信したコンテンツを使い回すキャッシュを組み合わせることで、大量のリクエストを分散して受け取るスケーラビリティが確保できるわけです。

CDNにおけるキャッシュは、ブラウザのキャッシュCloudFrontエッジキャッシュのキャッシュの2種類があります。これらのキャッシュが効き過ぎてしまう(≒使い回しの期間が長い)とオリジンでコンテンツを変更した場合もエッジキャッシュ(CloudFrontのキャッシュサーバー)やブラウザで古いコンテンツが保持されてしまうため、キャッシュ期間を適切に調整する必要があります。CloudFrontでは、以下の設定でそれぞれのキャッシュ期間を設定します。

  • ブラウザキャッシュ : オリジンのHTTPヘッダ
  • CloudFrontエッジキャッシュ : 3つのTTL設定

ブラウザキャッシュ : オリジンのHTTPヘッダ

一般的なWebブラウザでは、いくつかのHTTPレスポンスヘッダ(コンテンツの通信に付属する情報)を読み取り、それらに従ってローカルにキャッシュします。CloudFrontを経由する場合は、エッジキャッシュはオリジンの付与したそれらのヘッダを変更せずそのままブラウザに配信するため、CloudFrontの設定には依らずオリジンのHTTPヘッダによってブラウザキャッシュの保持期間が決まります。例えば、オリジンでWebサーバーのNginxを使用する場合はNginxのexpiresディレクティブに保持期間を設定します。

      :
    server {
        listen       80;
        server_name  localhost;
        root         /usr/share/nginx/html;
        expires 1h; # 保持期間は1時間
    }
      :

curlコマンド(CLIのWebクライアント)で試してみました。Nginxのexpiresディレクティブは設定した期間に合わせてExpiresヘッダとCache-Controlヘッダをレスポンスに付与します。

$ curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Thu, 18 Jun 2015 14:16:43 GMT
Content-Type: text/html
Content-Length: 3770
Last-Modified: Fri, 06 Mar 2015 03:53:02 GMT
Connection: keep-alive
ETag: "54f9249e-eba"
Expires: Thu, 18 Jun 2015 15:16:43 GMT
Cache-Control: max-age=3600
Accept-Ranges: bytes
$

Expireヘッダはタイムスタンプ形式のため、Dateヘッダの1時間後の時刻、Cache-Control: max-ageは秒単位なので、60秒*60分で1時間になっていることがわかりますね。ちなみに、CloudFrontはストレージサービスのS3をオリジンにすることもできますが、S3の場合はコンテンツ1つずつにHTTPヘッダが付与できるので、個別に上記ヘッダを設定してキャッシュ期間を制御します。複数のコンテンツへの一括設定は、以下のエントリーが参考になります。

CloudFrontエッジキャッシュ : 3つのTTL設定

CloudFrontのキャッシュサーバーであるエッジキャッシュも、オリジンからのコンテンツの初回取得後にローカルにキャッシュを保持します。コンテンツの保持期間はオリジンのCache-Control: max-ageをベースに3つのTTL設定(Minimum/Maximum/Default)を評価して決められます。評価ルールの詳細は清野のブログ記事を参考にしてください。このルールは以下の理由から、うまく使いこなすのが至難かと思います。

  • ルールが難しい
  • オリジンのCache-Control: max-ageの確認が大変

そこで、個人的には3つの設定を全て同値にすることをお奨めします。こうすることで、オリジンのヘッダに依らず固定的なCloudFrontのキャッシュ保持期間を設定することができます。

キャッシュ期間を1分(60秒)にする例

cfttl02

乱暴な!と思うかもしれませんが、実際の保持期間を順番に評価するのはトラブルシューティング時などは手間になりますし、コンテンツの特性に応じて複数のオリジン、キャッシュ設定を別々に持たせることもできますので、決め打ちにしても問題無いケースがほとんどなのでは、と思っています。

先ほどと同じく、curlコマンドで動作を確認してみます。オリジンは先ほど同じ設定のNginxです。CloudFrontでのキャッシュの扱いはX-Cacheヘッダで確認できます。

$ curl -I daomopd1xy7ix.cloudfront.net
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 3770
Connection: keep-alive
Server: nginx/1.6.2
Date: Thu, 18 Jun 2015 14:17:13 GMT
Last-Modified: Fri, 06 Mar 2015 03:53:02 GMT
ETag: "54f9249e-eba"
Expires: Thu, 18 Jun 2015 15:17:13 GMT
Cache-Control: max-age=3600
Accept-Ranges: bytes
X-Cache: Miss from cloudfront
Via: 1.1 c581b24c59d58843a4687e94287bf37b.cloudfront.net (CloudFront)
X-Amz-Cf-Id: USIFCYRodrkAqbTmWPsXNShPRHkb84kr3KeMm0sRnda4FiUGxLQi_Q==
$

CloudFrontのTTL設定にかかわらず、ExpiresヘッダとCache-Controlヘッダが付与されているのがわかりますね。X-Cacheヘッダは、初回アクセス(エッジキャッシュにキャッシュが無い)場合はMiss from cloudfrontという値になります。では、もう一度同じリクエストを発行してみます。

$ curl -I daomopd1xy7ix.cloudfront.net
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 3770
Connection: keep-alive
Server: nginx/1.6.2
Date: Thu, 18 Jun 2015 14:17:13 GMT
Last-Modified: Fri, 06 Mar 2015 03:53:02 GMT
ETag: "54f9249e-eba"
Expires: Thu, 18 Jun 2015 15:17:13 GMT
Cache-Control: max-age=3600
Accept-Ranges: bytes
Age: 15
X-Cache: Hit from cloudfront
Via: 1.1 dbce606a255ddeca27625b7e822db4ab.cloudfront.net (CloudFront)
X-Amz-Cf-Id: JLQjY7MVz_JeNpxZ7YXkT4uA3VvuqihO8Er9jSEG61qtkksaCIXeaQ==
$

今度はX-CacheヘッダがHit from cloudfrontになっていますね。これはエッジキャッシュが保持するキャッシュが返ってきていることを指します。この場合はオリジンへのリクエストが発生せず、ブラウザとエッジキャッシュ間で通信が完結したことになります。

ではTTL設定である1分だけ待ってから、再度リクエストしてみます。

$ curl -I daomopd1xy7ix.cloudfront.net
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 3770
Connection: keep-alive
Server: nginx/1.6.2
Date: Thu, 18 Jun 2015 14:17:13 GMT
Last-Modified: Fri, 06 Mar 2015 03:53:02 GMT
ETag: "54f9249e-eba"
Expires: Thu, 18 Jun 2015 15:17:13 GMT
Cache-Control: max-age=3600
Accept-Ranges: bytes
X-Cache: RefreshHit from cloudfront
Via: 1.1 359b389bb1ffde4de21013a9d11940b6.cloudfront.net (CloudFront)
X-Amz-Cf-Id: IvJmcGLigQEAe8noO4feeJwrb4gs0x4knb_RxxI2lk65sqsdSDERrQ==

X-CacheヘッダがRefreshHit from cloudfrontになっていますね。これはエッジキャッシュのキャッシュ保持期間が切れ、オリジンに再取得しに行ったことを示します。設定通りの動きになっていることがわかりますね。

キャッシュの削除(Invalidation)

誤ったコンテンツの公開など、一度キャッシュさせたコンテンツを削除したい場合にはInvalidationで削除することができます。ただ、Invalidationの適用には発行してから10〜15分かかることに注意してください。また、最近ワイルドカードでの指定がサポートされたので、このあたりの詳細は、清野のブログエントリー *1を参照ください。

まとめ

CloudFrontでのキャッシュの制御方法についてご紹介してみました。最近TTL設定のバリエーションが増えてできる範囲が広がりましたが、まずは基本的な扱い方を押さえておけば良いのではと思います。

脚注

  1. コンテンツに難あり