はじめに
CloudFrontでIP制限とレスポンスヘッダー操作を組み合わせた構成を作ろうとしたときに、思わぬ制約にハマりました。
やりたかったことはシンプルでした。
- ビューワーリクエスト:Lambda@Edge でIP制限をかける
- ビューワーレスポンス:CloudFront Functions でコンテンツタイプを強制する
「それぞれ別のイベントタイプだし、組み合わせても問題ないだろう」と思っていたのですが、これが動きませんでした。
ハマったこと
CloudFrontのディストリビューション設定を進めていくと、ビューワーレスポンスにCloudFront Functionsを紐付けようとした時点でエラーになりました。
エラーの内容を調べると、公式ドキュメントにしっかり書いてありました。
公式ドキュメントの制約
該当箇所を要約すると、以下の2つのルールがあります。
ルール1:各イベントタイプに紐付けられる関数は1つだけ
ビューワーリクエスト、オリジンリクエスト、オリジンレスポンス、ビューワーレスポンスの各イベントに対して、関数の関連付けは 1つのみ です。
ルール2:ビューワーイベントでは CloudFront Functions と Lambda@Edge を混在させることができない
ここが今回のハマりポイントです。同一キャッシュ動作において、ビューワーリクエストまたはビューワーレスポンスに CloudFront Functions と Lambda@Edge の両方を使うことはできません。
許可される組み合わせ・されない組み合わせ
公式ドキュメントの表を整理すると以下のようになります。
| Lambda@Edge のイベント | CloudFront Functions(ビューワーリクエスト) | CloudFront Functions(ビューワーレスポンス) |
|---|---|---|
| ビューワーリクエスト | ❌ 不可 | ❌ 不可 |
| オリジンリクエスト | ✅ 可 | ✅ 可 |
| オリジンレスポンス | ✅ 可 | ✅ 可 |
| ビューワーレスポンス | ❌ 不可 | ❌ 不可 |
つまり、どちらかのビューワーイベントで Lambda@Edge を使っていると、もう一方のビューワーイベントでも CloudFront Functions は使えない という制約です。
今回のケースに当てはめると:
- ビューワーリクエスト:Lambda@Edge(IP制限)← これを設定した
- ビューワーレスポンス:CloudFront Functions(コンテンツタイプ強制)← これが設定できない ❌
どう対処するか
回避策は大きく2つあります。
対処法1:CloudFront Functions に統一する
IP制限の処理をLambda@EdgeからCloudFront Functionsに移行します。
CloudFront FunctionsはJavaScriptで実装でき、実行時間・メモリの制限はLambda@Edgeより厳しいものの、シンプルなIP制限であれば十分対応可能です。
// CloudFront Functions(ビューワーリクエスト)でIP制限の例
function handler(event) {
var request = event.request;
var clientIp = event.viewer.ip;
var allowedIps = ['xxx.xxx.xxx.xxx', 'yyy.yyy.yyy.yyy'];
if (!allowedIps.includes(clientIp)) {
return {
statusCode: 403,
statusDescription: 'Forbidden',
};
}
return request;
}
ビューワーリクエストとビューワーレスポンスの両方をCloudFront Functionsに統一すれば、制約には引っかかりません。
対処法2:Lambda@Edge に統一する
コンテンツタイプ強制の処理をCloudFront FunctionsからLambda@Edgeのビューワーレスポンスに移行します。
こちらはNode.js / Python で実装でき、より複雑な処理にも対応できます。ただし、CloudFront Functionsと比べてコストとレイテンシがやや高くなる点は留意が必要です。
どちらを選ぶか
| 観点 | CloudFront Functions | Lambda@Edge |
|---|---|---|
| 実行時間 | 最大1ms | 最大5秒(ビューワーイベント) |
| メモリ | 2MB | 128MB〜 |
| コスト | 安い | やや高い |
| 対応ランタイム | JavaScript (ES5.1相当) | Node.js / Python |
| 向いている処理 | 軽量なヘッダー操作・リダイレクト・簡単なIP制限 | 複雑なロジック・外部API連携・大きなペイロード処理 |
シンプルなIP制限(固定IPのホワイトリスト)であれば、CloudFront Functionsで十分まかなえます。処理が複雑になりそうなら、Lambda@Edgeに統一するのが無難です。
まとめ
- 同一キャッシュ動作において、ビューワーイベント(ビューワーリクエスト・ビューワーレスポンス)ではCloudFront FunctionsとLambda@Edgeを混在させることができない
- ビューワーリクエストにLambda@Edgeを設定した場合、ビューワーレスポンスにCloudFront Functionsは設定できない(逆も同様)
- オリジンイベント(オリジンリクエスト・オリジンレスポンス)との組み合わせは問題なし
- 回避策は どちらかに統一する こと
この制約はAWSのコンソール上では設定しようとするまでエラーに気づきにくいので、設計段階で意識しておくと良いと思います。
公式ドキュメントに記載はあるのですが見落としがちな部分だったので、同じところでハマる方の参考になれば幸いです。