CloudFrontのキャッシュが効かない場合の原因診断と対策

CloudFrontを導入したのに期待通りキャッシュされない経験は多くのエンジニアが経験します。この記事では、キャッシュが機能しない具体的な原因6つと、それぞれの診断・対策方法を実装ベースで解説します。

CloudFrontのキャッシュが効かない主な原因

CloudFrontでキャッシュが効かないケースは、単純な設定ミスから複雑なヘッダー干渉まで多岐にわたります。以下、実務で頻出する6つの原因を順序立てて説明します。

1. キャッシュコントロールヘッダーの誤設定

症状と原因

最も多い原因は、オリジンサーバーがCache-Control: no-cacheCache-Control: no-storeを返している場合です。CloudFrontはこれらのヘッダーを尊重し、キャッシュを保存しません。

診断方法

CloudFront Consoleで対象のディストリビューションを開き、「Distribution Settings」から「Cache Behaviors」を確認してください。あるいはAWS CLIで以下を実行します:

# ディストリビューションIDを指定して詳細を取得
aws cloudfront get-distribution-config --id E1234ABCD

# 出力からCacheBehaviorsセクションのDefaultTTLを確認
# DefaultTTLが0になっていないか、MaxTTLが短くないか確認

対策

オリジンサーバーのレスポンスヘッダーを修正します。例えば、Node.js + Expressの場合:

// キャッシュ可能な静的ファイルの場合
app.use(express.static('public', {
  maxAge: '1d', // 1日間キャッシュさせる
  setHeaders: function(res, path) {
    res.setHeader('Cache-Control', 'public, max-age=86400');
  }
}));

// APIレスポンスでキャッシュを明示的に許可する場合
app.get('/api/data', (req, res) => {
  res.setHeader('Cache-Control', 'public, max-age=3600');
  res.json({ data: 'example' });
});

Pythonの場合(Django):

from django.views.decorators.cache import cache_page
from django.http import HttpResponse

# 3600秒(1時間)キャッシュさせる
@cache_page(3600)
def cached_view(request):
    response = HttpResponse('cached content')
    response['Cache-Control'] = 'public, max-age=3600'
    return response

2. CloudFront側でキャッシュがバイパスされている

症状と原因

CloudFront Console内の「Cache Behaviors」設定で「Caching Policy」が「Disabled」になっているか、デフォルトのManaged Caching Policyを使用していない場合、キャッシュが無効化されます。

診断方法

以下のコマンドで現在のポリシー設定を確認します:

aws cloudfront get-distribution-config --id E1234ABCD | \
  jq '.DistributionConfig.CacheBehaviors[0].CachePolicyId'

# CachePolicyIdが空(null)または特定のIDでない場合は無効
# または ForwardedValues セクションで Querystring Forwarding が 
# "all" になっていないか確認

対策

CloudFront Consoleで推奨キャッシュポリシー「Managed-CachingOptimized」を適用するか、カスタムポリシーを作成します。AWS CLIでの設定例:

# キャッシュポリシーの一覧を確認
aws cloudfront list-cache-policies --type managed

# マネージドポリシーIDを取得(CachingOptimizedの場合)
# 出力例: "658327ea-f89d-4fab-a63d-7e88639e58f6"

# その後、ディストリビューション設定でCachePolicyIdを指定

3. クエリストリングやクッキーがキャッシュキーに含まれていない

症状と原因

同一URLでもクエリストリングやクッキーが異なる場合、CloudFrontはそれらを区別してキャッシュキーを生成します。設定により、これらが無視されてしまい、意図しない旧データが返される可能性があります。

診断とコンソール確認

CloudFront Console → 対象ディストリビューション → Cache behaviors → Query string forwarding を確認してください。以下のように設定されていることを確認します:

# AWS CLIで確認
aws cloudfront get-distribution-config --id E1234ABCD | \
  jq '.DistributionConfig.CacheBehaviors[0].ForwardedValues.QueryString'

# 結果が true なら、クエリストリングがキャッシュキーに含まれる
# false なら、すべてのクエリストリングが無視される(注意!)

対策

特定のクエリパラメータのみキャッシュキーに含めたい場合、Whitelist機能を使用します:

aws cloudfront create-cache-policy \
  --cache-policy-config '{
    "Name": "CustomCachePolicy",
    "Comment": "Query string specific caching",
    "DefaultTTL": 3600,
    "MaxTTL": 86400,
    "MinTTL": 1,
    "ParametersInCacheKeyAndForwardedToOrigin": {
      "QueryStringsConfig": {
        "QueryStringBehavior": "whitelist",
        "QueryStrings": {
          "Items": ["version", "uid"]
        }
      },
      "HeadersConfig": {
        "HeaderBehavior": "none"
      },
      "CookiesConfig": {
        "CookieBehavior": "none"
      }
    }
  }'

4. Origin Shield有効化による遅延

症状と原因

Origin Shieldを有効にしている場合、追加の中間キャッシュレイヤーを経由するため、初回リクエストの応答がさらに遅くなります。これはキャッシュ「未ヒット」時の振る舞いであり、キャッシュが効かないのではなく、キャッシュ構築に時間がかかる状態です。

診断方法

CloudFrontのレスポンスヘッダーを確認します:

# curlでレスポンスヘッダーを確認
curl -I https://your-cdn.cloudfront.net/asset.jpg

# X-Cache: Miss from cloudfront (CloudFront キャッシュミス)
# X-Cache: Hit from cloudfront (CloudFront キャッシュヒット)
# X-Cache: Hit from cloudfront; origin shield hit (Origin Shield ヒット)

対策

Origin Shieldは追加コストがかかりますが、キャッシュヒット率が大幅に向上します。ただし、不要な場合は以下で無効化できます:

aws cloudfront update-distribution \
  --id E1234ABCD \
  --distribution-config file://distribution-config.json

# distribution-config.json内で
# "OriginShield": {"Enabled": false}

5. Vary ヘッダーの過度な使用

症状と原因

VaryヘッダーはCloudFrontにキャッシュキーの一部として特定ヘッダーを含めるよう指示します。例えば Vary: User-Agent なら、ユーザーエージェントごとに異なるキャッシュが作成され、キャッシュヒット率が低下します。

確認方法

curl -I https://your-cdn.cloudfront.net/page.html | grep Vary

# 出力例:
# Vary: Accept-Encoding, User-Agent, Accept-Language
# このように複数のVaryが指定されていると、キャッシュヒット率が低下

対策

必要最小限のVaryヘッダーのみ設定します。通常は Accept-Encoding のみで十分です:

// Node.js + Express
app.use((req, res, next) => {
  res.setHeader('Vary', 'Accept-Encoding');
  next();
});

# または Nginxの場合
add_header Vary "Accept-Encoding" always;

6. TTL(Time To Live)が短すぎる

症状と原因

DefaultTTLが数秒以下に設定されている場合、キャッシュは頻繁に期限切れになり、常にオリジンサーバーにアクセスします。これは実質的にキャッシュが機能していない状態です。

診断方法

aws cloudfront get-distribution-config --id E1234ABCD | \
  jq '.DistributionConfig.CacheBehaviors[].DefaultTTL'

# 結果が 0, 1, 5 など極めて小さい値なら問題あり

対策

コンテンツの特性に合わせてTTLを設定します:

aws cloudfront create-cache-policy \
  --cache-policy-config '{
    "Name": "OptimizedCachePolicy",
    "DefaultTTL": 86400,    # 1日
    "MaxTTL": 31536000,     # 1年
    "MinTTL": 1,            # 最小1秒
    "ParametersInCacheKeyAndForwardedToOrigin": {
      "QueryStringsConfig": {"QueryStringBehavior": "none"},
      "HeadersConfig": {"HeaderBehavior": "none"},
      "CookiesConfig": {"CookieBehavior": "none"}
    }
  }'

実践的なデバッグ手順

キャッシュが効かない場合は、以下の順序で診断してください:

# ステップ1: レスポンスヘッダーを確認
curl -I https://your-cdn.cloudfront.net/asset.jpg

# X-Cache ヘッダーを確認(Hit/Miss)
# Cache-Control ヘッダーを確認

# ステップ2: CloudFront ディストリビューション設定を確認
aws cloudfront get-distribution-config --id E1234ABCD > dist-config.json

# ステップ3: キャッシュポリシーを確認
aws cloudfront get-cache-policy --id [PolicyId]

# ステップ4: オリジンサーバーのヘッダーを直接確認
curl -I http://[OriginDomain]/asset.jpg

CloudFrontキャッシュの動作確認テスト

以下はキャッシュが正常に機能しているか確認するテストスクリプト(Python)です:

import requests
import time

CDN_URL = "https://your-cdn.cloudfront.net/test.json"

def test_cache():
    print("=== CloudFront キャッシュテスト ===\n")
    
    # 第1回目リクエスト(キャッシュミス期待)
    resp1 = requests.head(CDN_URL)
    cache_status_1 = resp1.headers.get('X-Cache', 'Unknown')
    age_1 = resp1.headers.get('Age', '0')
    print(f"1回目リクエスト:")
    print(f"  X-Cache: {cache_status_1}")
    print(f"  Age: {age_1}")
    print(f"  Cache-Control: {resp1.headers.get('Cache-Control', 'Not set')}\n")
    
    time.sleep(2)
    
    # 第2回目リクエスト(キャッシュヒット期待)
    resp2 = requests.head(CDN_URL)
    cache_status_2 = resp2.headers.get('X-Cache', 'Unknown')
    age_2 = resp2.headers.get('Age', '0')
    print(f"2回目リクエスト:")
    print(f"  X-Cache: {cache_status_2}")
    print(f"  Age: {age_2}\n")
    
    if 'Hit' in cache_status_2:
        print("✓ キャッシュ正常に機能しています")
    else:
        print("✗ キャッシュが効いていません")
        print("  上記の原因1-6を確認してください")

if __name__ == "__main__":
    test_cache()

よくある質問

Q1: CloudFront経由でも常にオリジンにアクセスされています。どうすればよいですか?

A:

K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →