【AWS】CloudFrontのキャッシュ有効期限（TTL）制御におけるCloudFrontのCache Policyとオリジンヘッダの使い分け

# はじめに
 CloudFrontにはコンテンツをキャッシュして、ユーザーから最も近いエッジロケーションからコンテンツを配信させることができます。このキャッシュ機能ですが、有効期限（TTL）についてはCloudFront側とオリジン側の両方で制御することが可能です。両方で制御できるのは便利である反面、複雑になりがちです。今回は、TTLに関するベストプラクティスを紹介しながら、TTL制御の使い分けについて読み解いていきます。
 
# 目次
[1.この記事を読んでほしい人](#1-この記事を読んでほしい人)
[2.概要](#2-概要)
[3.CloudFrontによるキャッシュ有効期限の制御](#3-cloudfrontによるキャッシュ有効期限の制御)
[4.設計思想（ベストプラクティスの紹介）](#4-設計思想ベストプラクティスの紹介)
[5.ベストプラクティスの考察](#5-ベストプラクティスの考察)
[6.まとめ](#6-まとめ)
[7.参考資料](#7-参考資料)
 
# 1. この記事を読んでほしい人
- CloudFrontのキャッシュ機能を利用しようとしている人（クライアントのブラウザ等へのキャッシュは対象外）
- キャッシュ有効期限の制御について、CloudFrontとオリジンでの設定の違いや使い分けについて詳しく知りたい人

# 2. 概要
本記事で想定しているコンテンツ配信の構成は以下の通りです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3895037/03903a6f-8b62-844c-a53c-27a14c6a8d97.png" width="500">
CloudFrontによるキャッシュの設定は主にCache Policyにて行いますが、TTLの設定に応じて有効期限が切れていないキャッシュを応答することになります。このTTLの設定はCloudFront側とオリジン側の両方で実施することが可能になっています。
CloudFrontによるキャッシュから応答する場合には以下のような流れとなります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3895037/5ed47244-8d5e-3073-3c15-964dc7e2591f.png" width="500">
本記事では、TTL制御におけるCloudFrontのCache Policyとオリジンヘッダの使い分けについて詳しく見ていきます。

# 3. CloudFrontによるキャッシュ有効期限の制御
冒頭でも紹介した通り、CloudFront側とオリジン側の両方でTTLの制御が可能です。
[AWS公式ドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Expiration.html)によれば、CloudFrontのCache PolicyとオリジンからのCache-Control/Expiresヘッダの関係性は以下の通りです。

| オリジンヘッダ | Cache Policy 最小 TTL = 0秒 | Cache Policy 最小 TTL > 0秒 |
|-----------------|--------------|-------------|
| Cache-Control max-ageを指定 | 指定された max-age と最⼤ TTL で小さい値の期間キャッシュ | <br>- 最小 TTL < max-age < 最大 TTL の場合、max-age期間<br>- max-age < 最小TTLの場合、最小TTL期間<br>- 最大 TTL < max-age の場合、最大TTL期間 |
| Cache-Control max-age と s-maxage を指定 | 指定された s-maxage と最⼤ TTL で小さい値の期間キャッシュ | <br>- 最小 TTL < s-maxage < 最大 TTL の場合、s-maxage期間<br>- s-maxage < 最小TTLの場合、最小TTL期間<br>- 最大 TTL < s-maxage の場合、最大TTL期間 |
| Expires を指定 | 指定された Expires ⽇付と最⼤ TTL で早い⽇付の期間キャッシュ | <br>- 最小 TTL < 最大 TTL の場合、Expires 日付<br>- Expires < 最小TTLの場合、最小TTL期間<br>- 最大 TTL < Expires の場合、最大TTL期間 |
| Cache-Control no-cache, no-store 、及び (又は) private ディレクティブを指定 | ヘッダを優先させる | 最⼩ TTL の期間キャッシュ |
| Cache-Control 設定なし | デフォルト TTL 期間キャッシュ | 最⼩ TTL 又はデフォルト TTL で ⼤きい値の期間キャッシュ |

※ S3 がオリジンの場合は S3 オブジェクト Metadata に Cache-Control, Expires を指定可能です。
※表中に登場する、Cache Controlヘッダのmax-age、s-maxage、no-cache、no-storeディレクティブ、およびExpiresヘッダについては以下の通りです。
- max-age：リソースが新鮮であると見なされる最大時間（秒単位）を指定します。このディレクティブが設定されている場合、クライアントはこの期間中、サーバーに再検証せずにキャッシュされたリソースを使用できます。
- s-maxage：共有キャッシュ（プロキシなど）に対してのみ適用される max-age の値を指定します。このディレクティブが存在する場合、共有キャッシュではこの値が max-age よりも優先されます。
- no-cache：キャッシュされたコピーを使用する前に、必ずオリジンサーバーに再検証を要求します。キャッシュの使用を禁止するものではなく、使用前に必ず再検証を要求します。
- no-store：レスポンスをキャッシュに保存することを禁止します。セキュリティやプライバシーに関連する情報を含むレスポンスに使用されます。
- Expires：リソースが期限切れになる日時を指定します。max-age が指定されている場合、Expires ヘッダは無視されます。

https://datatracker.ietf.org/doc/html/rfc7234#section-5

# 4. 設計思想（ベストプラクティスの紹介）
[AWS公式ドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Expiration.html)からの引用となりますが、キャッシュ有効期限の変更について以下のように記載されています。
>デフォルトでは、各ファイルは 24 時間後に自動的に有効期限切れになりますが、2 つの方法でこのデフォルトの動作を変更できます。
>- 同じパスパターンに一致するすべてのファイルのキャッシュ保持期間を変更するには、CloudFront の設定でキャッシュの動作の [Minimum TTL (最小 TTL)]、[Maximum TTL (最大 TTL)]、[Default TTL (デフォルト TTL)] を変更できます。個々の設定については、「ディストリビューション設定リファレンス」の「最小 TTL」、「最大 TTL」、「デフォルト TTL」を参照してください。
>- 個々のファイルのキャッシュ保持期間を変更するには、ファイルに Cache-Control または max-age ディレクティブが付いた s-maxage を追加するか、Expires ヘッダーフィールドを追加するようにオリジンを設定します。詳細については、「ヘッダーを使用して個々のオブジェクトのキャッシュ保持期間を制御する」を参照してください。

# 5. ベストプラクティスの考察
ベストプラクティスに従うと、キャッシュ対象がパスパターン単位か個々のファイル単位かで、制御方法を使い分けると良さそうです。全てのキャッシュ対象がパスパターン単位で制御する場合には、CloudFrontのCache Policyのみを使えば良く、ファイル単位で制御する場合にはオリジンヘッダで細かく制御していくことになります。フロー図にまとめてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3895037/fc18efa8-e3fd-2075-237a-8e2836bbe945.png)

＜補足＞
- Cache-Control max-ageとExpiresの両方を指定した場合、max-ageが優先される。また、s-maxageを指定した場合、max-ageとExpiresの両方を上書きする。
- max-ageとs-maxageのキャッシュ有効範囲の違い
    - max-age：クライアントのブラウザ等までキャッシュが有効
    - s-maxage：クライアントのブラウザ等まではキャッシュされず、サーバとクライアント間にあるキャッシュサーバまでキャッシュが有効

※キャッシュサーバがCloudFrontの場合で図示すると以下の通り。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3895037/84a82ff4-dceb-7f5d-47db-9aa74fc6fbd8.png" width="400">

# 6. まとめ
CloudFrontのキャッシュ有効期限（TTL）制御は、CloudFront側とオリジン側の両方で可能です。パスパターン単位の制御にはCloudFrontのCache Policyを、ファイル単位の細かい制御にはオリジンヘッダを使用するのがベストプラクティスです。オリジンヘッダを使う場合には、そのキャッシュ要件に基づいてどのディレクティブを使用するのかを見極める必要があります。本ブログで紹介したフロー図等も参考に適切な制御方法を選択することが求められます。

# 7. 参考資料
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Expiration.html

https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudFront-CacheControl_0430_v1.pdf

https://datatracker.ietf.org/doc/html/rfc7234#section-5

:::note warn
本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。
:::