CloudFrontがタグベースのキャッシュInvalidationに対応したので試してみた
2026年4月29日、Amazon CloudFrontがキャッシュタグによるInvalidationに対応しました。
従来のCloudFront Invalidationでは、URLパスを個別に指定するか、ワイルドカード(/*)で広範囲にキャッシュを消すしかありませんでした。今回のアップデートにより、オリジンのレスポンスヘッダーでオブジェクトにタグを付与し、そのタグを指定して一括Invalidationできるようになっています。
S3オリジン+CloudFormationで検証環境を構築し、実際の動作を確認してみました。
キャッシュタグInvalidationの仕組み
公式ドキュメントの内容をもとに、仕組みを整理します。
3ステップで利用開始
- ディストリビューションに
CacheTagConfigを設定: タグを読み取るHTTPヘッダー名を指定します - オリジンがレスポンスヘッダーにタグを付与: カンマ区切りで複数タグを指定できます
#タグ名でInvalidation:--paths "#version:v1"のように#プレフィックスで指定します
S3オリジンの場合、オブジェクトのメタデータにタグを設定するだけで利用できます。S3が x-amz-meta-<キー名> ヘッダーとして自動返却する仕組みです。
S3オブジェクトのメタデータ:
cache-tag: "page:products, version:v1, category:electronics"
↓ S3が自動変換
レスポンスヘッダー:
x-amz-meta-cache-tag: page:products, version:v1, category:electronics
↓ CloudFrontが読み取り
キャッシュタグとして保存: [page:products] [version:v1] [category:electronics]
タグの仕様
| 項目 | 制限 |
|---|---|
| 1オブジェクトあたりの最大タグ数 | 50(超過分は無視) |
| 1タグの最大文字数 | 256 |
| 使用可能文字 | ASCII 33〜126(スペース・カンマ・制御文字不可) |
| 大文字小文字 | 区別なし |
※ タグ値そのものにカンマやスペースは含められません。カンマはタグ間の区切り文字として使用され、区切り前後のスペースはパース時にトリムされます。つまり tag1,tag2 と tag1, tag2 は同等に扱われます。
料金
タグ1つ = パス1つ分として課金されます。従来のパスInvalidationと同じ料金体系(月1,000パスまで無料、以降1パスあたり$0.005)です。
ポイントは、タグに紐づくオブジェクトが100個でも1万個でも、タグ指定1回 = 1パス分の課金で済むことです。
クォータ
| 項目 | 制限 |
|---|---|
| パスまたはタグの発行レート | 毎秒150件(パス・タグ合算) |
| ワイルドカードInvalidation | 毎秒1件 |
| 1タグあたりの処理対象ファイル数 | 上限なし |
タグはパスと同じクォータを共有し、タグ1つが1パスとしてカウントされます。詳細は公式のクォータページを参照してください。
検証環境の構築(CloudFormation)
CloudFormationで検証環境を構築します。CacheTagConfig はCloudFormationでサポートされています。
CloudFormation テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFront Cache Tag Invalidation Test Stack
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'cf-cache-tag-test-${AWS::AccountId}'
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowCloudFrontServicePrincipal
Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action: s3:GetObject
Resource: !Sub '${S3Bucket.Arn}/*'
Condition:
StringEquals:
AWS:SourceArn: !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/${Distribution}'
OAC:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: !Sub 'cache-tag-test-oac-${AWS::AccountId}'
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Cache Tag Invalidation Test
Enabled: true
DefaultRootObject: index.html
CacheTagConfig:
HeaderName: x-amz-meta-cache-tag
Origins:
- Id: S3Origin
DomainName: !GetAtt S3Bucket.RegionalDomainName
OriginAccessControlId: !Ref OAC
S3OriginConfig:
OriginAccessIdentity: ''
DefaultCacheBehavior:
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
Compress: true
ForwardedValues:
QueryString: false
DefaultTTL: 3600
MinTTL: 180
MaxTTL: 3600
Outputs:
BucketName:
Value: !Ref S3Bucket
DistributionId:
Value: !Ref Distribution
DomainName:
Value: !GetAtt Distribution.DomainName
今回のアップデートのコア設定は CacheTagConfig です。
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
CacheTagConfig:
HeaderName: x-amz-meta-cache-tag
S3メタデータのキー cache-tag が x-amz-meta-cache-tag ヘッダーとして返却されるので、HeaderName にそれを指定しています。
キャッシュ設定にはマネージドキャッシュポリシー(CachingOptimized)ではなく、レガシー設定(ForwardedValues)を使用しています。検証時、CachingOptimized ではS3オリジンが Cache-Control ヘッダーを返さない場合にCloudFrontが条件付きリクエストでオリジンに問い合わせてしまい、キャッシュが安定しない挙動がありました。レガシー設定でMinTTLを180秒に設定し、S3側にも Cache-Control: max-age=300 を付与することで、オリジンのキャッシュ指示を尊重しつつ安定したキャッシュ動作を確認できています。
デプロイ
aws cloudformation deploy \
--stack-name cf-cache-tag-test \
--template-file template.yaml \
--region us-east-1
テスト用コンテンツのアップロード
S3オブジェクトにメタデータ(キャッシュタグ)と Cache-Control ヘッダーを付与してアップロードします。
aws s3api put-object --bucket "$BUCKET" --key products.html \
--body products.html --content-type text/html \
--cache-control "max-age=300" \
--metadata '{"cache-tag":"page:products, version:v1, category:electronics"}'
--metadata の cache-tag キーに、カンマ区切りでタグを指定しています。他のファイルも同様にアップロードします。
動作検証
2つのテストケースで動作を確認します。
CloudFormationのOutputsからディストリビューションIDを取得しておきます。
DIST_ID=$(aws cloudformation describe-stacks \
--stack-name cf-cache-tag-test \
--query "Stacks[0].Outputs[?OutputKey=='DistributionId'].OutputValue" \
--output text)
テスト構成
| ファイル | タグ |
|---|---|
| index.html | page:home, version:v1 |
| products.html | page:products, version:v1, category:electronics |
| about.html | page:about, version:v1 |
3ファイルすべてが version:v1 タグを共有し、page: で始まるタグはファイルごとに異なります。
TEST A: 共通タグで一括Invalidation(#version:v1)
3ファイルすべてが version:v1 タグを持っています。各ファイルに複数回アクセスしてキャッシュを温めた上で、このタグを指定してInvalidationを実行します。
aws cloudfront create-invalidation \
--distribution-id "$DIST_ID" \
--paths "#version:v1"
パスの代わりに # プレフィックス付きのタグ名を指定するだけです。# はシェルでコメントとして解釈されるため、ダブルクォートで囲む必要があります。
結果
| ファイル | Invalidation前 | Invalidation後 |
|---|---|---|
| index.html | Hit | Miss ✅ |
| products.html | Hit | Miss ✅ |
| about.html | Hit | Miss ✅ |
※ X-Cache: Miss from cloudfront はキャッシュが無効化され、オリジンから再取得されたことを示します。
全ファイルのキャッシュが無効化されました。Invalidationの発行から完了まで約18秒でした(公式ドキュメントではP95で25秒未満)。
TEST B: 選択的Invalidation(#page:products)
次に、キャッシュを再度温めた上で、page:products タグを指定してInvalidationを実行します。このタグを持つのは products.html のみです。
aws cloudfront create-invalidation \
--distribution-id "$DIST_ID" \
--paths "#page:products"
結果
| ファイル | Invalidation前 | Invalidation後 |
|---|---|---|
| index.html | Hit | Hit ✅ |
| products.html | Hit | Miss ✅ |
| about.html | Hit | Hit ✅ |
products.html だけがキャッシュ無効化され、index.html と about.html はキャッシュヒットを維持しています。発行から完了まで約22秒でした。
これが従来のワイルドカード(/products/*)との決定的な違いです。タグで意味的にグルーピングすることで、無関係なコンテンツのキャッシュを消さずに済みます。
従来手法との比較
| 従来(パス指定) | キャッシュタグ | |
|---|---|---|
| 指定方法 | /products/123.html や /products/* |
#brand:acme |
| 精度 | パス構造に依存。無関係なコンテンツを巻き込みやすい | 意味的なグルーピングで必要なものだけ無効化 |
| 運用負荷 | 対象URLの追跡・列挙が必要 | アプリ側でタグを付けておけば1リクエストで完了 |
| キャッシュヒット率 | ワイルドカードで下がりやすい | 関係ないキャッシュが残るので高く維持 |
| オリジン負荷 | 消しすぎた分だけ再取得が発生 | 最小限の再取得で済む |
| コスト | 個別URL列挙はパス数分課金。ワイルドカードは1パス分だが、オリジン転送料が増大 | タグ1つ=1パス分の課金。紐づくオブジェクト数に関係なく定額。オリジン転送料も最小限 |
キャッシュタグにより、「何を消すか」の判断がURL構造からアプリケーションの意味(ブランド、バージョン、ユーザーなど)に移るのが本質的な変化です。
タグ設計のTips
公式ドキュメントでは以下のユースケース例が紹介されています。
| ユースケース | タグ例 | Invalidation例 |
|---|---|---|
| ECサイトの商品カタログ | category:electronics, brand:acme, product:12345 |
#brand:acme でACME全商品を一括無効化 |
| ユーザー生成コンテンツ | user:12345, content-type:image |
#user:12345 で退会ユーザーのコンテンツを一括削除 |
| コンテンツバージョニング | version:v2, template:homepage |
#version:v2 でデプロイ時にv2コンテンツを一括無効化 |
いずれも キー:値 形式で統一されています。これに倣い、自分のプロジェクトでもプレフィックスをルール化しておくと運用時に迷いません。1オブジェクトに複数タグを付与できるので、複数の軸で横断的にInvalidationできます。
注意点
- オプトイン必須:
CacheTagConfig未設定のディストリビューションではタグが無視されます - ヘッダー名の変更に注意:
CacheTagConfigのヘッダー名を変更すると、旧ヘッダーでキャッシュされたオブジェクトにはタグInvalidationが効かなくなります。変更前にパスInvalidation(/*)が必要です - タグ変更は既存キャッシュに反映されない: S3メタデータのタグを変更しても、CloudFrontに既にキャッシュされたオブジェクトのタグは更新されません。新しいタグでInvalidationするには、先にパスInvalidationで既存キャッシュを削除し、新しいタグ付きのレスポンスを再キャッシュさせる必要があります
- CacheTagConfig削除時: 削除しても既存キャッシュはTTL満了まで通常通り配信されます
- 後方互換: 従来のパス・ワイルドカードInvalidationは引き続き利用可能です
- 全バリアントが対象: タグでInvalidationすると、Cookie・ヘッダー・クエリ文字列によるキャッシュバリアントもすべて無効化されます。特定のバリアントだけを残すことはできません
まとめ
CloudFrontがキャッシュタグによるInvalidationに対応し、URL構造に依存しない柔軟なキャッシュ管理が可能になりました。
今回の検証では、S3オブジェクトのメタデータにタグを設定し、#version:v1 で3ファイル一括無効化、#page:products で1ファイルだけ選択的に無効化できることを確認しました。ワイルドカードによるキャッシュの消しすぎを避け、キャッシュヒット率を高く維持しながら、必要なコンテンツだけを精密にInvalidationできます。
CloudFormationにも対応しており、従来のパスInvalidationとの併用もできるので、段階的に導入できます。
今回はS3オリジンで検証しましたが、より効果を発揮するのは動的コンテンツを配信するCMSオリジンのケースです。
例えば、記事の更新時にCMSがレスポンスヘッダーで article:12345, category:tech, author:suzuki のようなタグを返すように設定したとします。これにより、特定カテゴリの記事一括更新や、著者の所属変更に伴うプロフィール情報の差し替えを、#category:tech や #author:suzuki の1リクエストで即座に反映できるようになります。
コンテンツの更新タイムラグを最小化しつつ、無関係なキャッシュを維持できるため、CMSとCloudFrontの組み合わせでキャッシュ戦略に悩んでいた場面での活用が期待できます。
著者タグ・カテゴリタグでのInvalidation
4つの記事をS3にアップロードし、著者タグ・カテゴリタグでの選択的Invalidationを確認しました。
追加検証(TEST C / TEST D)
4つの記事ページに著者・カテゴリのタグを付与してアップロードします。
| ファイル | タグ |
|---|---|
| articles/001.html | article:001, category:compute, author:suzuki |
| articles/002.html | article:002, category:networking, author:suzuki |
| articles/003.html | article:003, category:database, author:tanaka |
| articles/004.html | article:004, category:storage, author:tanaka |
TEST C: 著者タグで一括Invalidation(#author:suzuki)
aws cloudfront create-invalidation --distribution-id "$DIST_ID" --paths "#author:suzuki"
| 記事 | 著者 | Invalidation後 |
|---|---|---|
| 001 (Lambda入門) | suzuki | Miss ✅ |
| 002 (CloudFront設計) | suzuki | Miss ✅ |
| 003 (DynamoDB実践) | tanaka | Hit ✅ |
| 004 (S3セキュリティ) | tanaka | Hit ✅ |
suzukiの記事2件だけが無効化され、tanakaの記事はキャッシュヒットを維持しています。
TEST D: カテゴリタグで選択的Invalidation(#category:database)
aws cloudfront create-invalidation --distribution-id "$DIST_ID" --paths "#category:database"
| 記事 | カテゴリ | Invalidation後 |
|---|---|---|
| 001 | compute | Hit ✅ |
| 002 | networking | Hit ✅ |
| 003 | database | Miss ✅ |
| 004 | storage | Hit ✅ |
同じ記事群に対して、著者軸でもカテゴリ軸でも、狙った記事だけを無効化できることを確認しました。







