はじめに
CloudFront Functions(以後CF2)は、L@Eと異なり、軽量のため、L@Eより優先して使用を検討する必要がようなので、CF2について使用パターンをまとめました。
ビューアーレスポンスは使用しなかったため、ビューアーリクエストのみのパターンになります。
CF2とL@Eの比較
1 | 2 | 3 |
---|---|---|
CloudFront Functions | Lambda@Edge | |
ランタイムサポート | JavaScript(ECMAScript 5.1準拠) | Node.js、Python |
実行場所 | 218 以上の CloudFrontエッジロケーション | 13 の CloudFrontリージョンのエッジキャッシュ |
CloudFront トリガー | ビューアリクエスト / ビューアレスポンス | ビューアリクエスト / ビューアレスポンス / オリジンリクエスト / オリジンレスポンス |
最大実行時間 | 1 ミリ秒未満 | 5 秒 (ビューアトリガー) / 30 秒 (オリジントリガー) |
最大メモリ | 2 MB | 128 MB (ビューアトリガー) / 10 GB (オリジントリガー) |
合計パッケージサイズ | 10 KB | 1 MB (ビューアトリガー) / 50 MB (オリジントリガー) |
ネットワークアクセス | なし | あり |
ファイルシステムアクセス | なし | あり |
リクエスト本文へのアクセス | なし | あり |
料金 | 無料利用枠あり。リクエストごとに課金。 | 無料利用枠なし。リクエストと関数の実行時間ごとに課金。 |
デプロイ反映時間 | 10秒以下 | 5分程度 |
デプロイ方法 | 複数を一度にデプロイ可能 | 1つずつデプロイ可能 |
CF2は、JavaScript(ECMAScript 5.1準拠)のため、constなど使用できない制限があります。下記で使用できる機能が挙げられています。
CF2の方が、軽量、デプロイ反映が早い、という点で始めに検討するとよいかと思います。
IP制限
許可したIPの場合、リクエストが通り、NGの場合403をレスポンスします
function handler(event) {
var request = event.request;
var clientIP = event.viewer.ip;
// 許可するIPを指定
var IP_WHITE_LIST = ["xxx.xxx.xxx.xxx", "xxx.xxx.xxx.xxx"];
var allowIP = IP_WHITE_LIST.includes(clientIP);
if (allowIP) {
return request;
} else {
var response = {
statusCode: 403,
statusDescription: "Forbidden",
};
return response;
}
}
アクセス承認
Bearer認証やBasic認証でCF2は使用できます。
Bearer認証
Bearerのトークンパラメータが成功した場合、リクエストが通り、失敗した場合、401エラーをレスポンスという処理になっています。
function handler(event) {
var request = event.request;
var headers = request.headers;
var authString = "Bearer <token値>";
if (
typeof headers.authorization === "undefined" ||
headers.authorization.value !== authString
) {
return {
statusCode: 401,
statusDescription: "Unauthorized",
headers: { "www-authenticate": { value: "Bearer" } },
};
}
console.log("request-success");
return request;
}
ローカルで認証できているか確認する場合、curlを使いましょう。
$ curl https://www.hoge.com/test/ \
-H "Authorization:Bearer token値"
Basic認証
Basic認証のトークンパラメータが成功した場合、リクエストが通り、失敗した場合、401エラーを返すという処理になっています。
function handler(event) {
var request = event.request;
var headers = request.headers;
// echo -n user:pass | base64
var authString = "Basic <token値>";
if (
typeof headers.authorization === "undefined" ||
headers.authorization.value !== authString
) {
return {
statusCode: 401,
statusDescription: "Unauthorized",
headers: { "www-authenticate": { value: "Basic" } }
};
}
console.log("request-success");
return request;
}
URLの書き換え
/test/
のパスを/fuga/
に書き換える
function handler(event) {
var request = event.request;
var uri = request.uri;
if (uri.startsWith("/test/")) {
request.uri = "/fuga/";
}
return request;
}
CFの代替ドメイン名 (CNAME)に記載がないホスト名は、ビューアーリクエストで書き変えることは、基本的にできません。
CNAME に複数指定するよう *.hoge.com
等の対応するとできますが、それ以外の方法でホストを書き換えたい場合、L@Eのオリジンリクエストで実現できます。
サブディレクトリのドキュメントルートを指定
静的サイトをS3に構築した場合、サブドメイン名と同じS3のディレクトリをルーティングできます。
fuga.hoge.com
の場合、S3のあるバケットのfuga
ディレクトリを指定する。
function handler(event) {
var request = event.request;
var uri = request.uri;
var host = request.headers.host.value;
var prefix = host.split(".", 1)[0];
if (uri.startsWith("/")) {
request.uri = "/" + prefix + uri;
} else {
request.uri = "/" + prefix + "/" + uri;
}
return request;
}
リダイレクト
URLのリダイレクト
特定のパスのみリダイレクト
/test/
配下のパスは、/test/403.html
にリダイレクトさせる
function handler(event) {
var request = event.request;
var host = request.headers.host.value;
var uri = request.uri;
if (uri.startsWith("/test/") && uri != "/test/403.html") {
return {
statusCode: 301,
statusDescription: "Moved Permanently",
headers: { location: { value: `https://${host}/test/403.html` } },
};
}
return request;
}
必ずリダイレクトさせる
必ず/test/403.html
にリダイレクトさせる
function handler(event) {
var request = event.request;
var host = request.headers.host.value;
var uri = request.uri;
if (uri != "/test/403.html") {
return {
statusCode: 301,
statusDescription: "Moved Permanently",
headers: { location: { value: `https://${host}/test/403.html` } },
};
}
return request;
}
リダイレクトの場合、ホストを変えることは、もちろん可能です。
403を返す
403を返す
function handler(event) {
var response = {
statusCode: 403,
statusDescription: "Forbidden",
};
return response;
}
wwwなしのhostをwwwにリダイレクトさせる
hoge.com
をリクエストした場合、www.hoge.com
にリダイレクトさせます。
CloudFrontには、元々xxx.cloudfront.net
というドメインが使用されているため、そのドメインでないことを判定に含めます。
function handler(event) {
var request = event.request;
var host = request.headers.host.value;
var uri = request.uri;
if (!host.includes('cloudfront.net') && !host.startsWith('www')) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: { location: { value: `https://www.${host}${uri}` } },
};
}
return request;
}
ルートディレクトリをindex.phpに指定
fuga.com/test/
やfuga.com/test`の場合、index.htmlがデフォルトで表示されるようにします。
function handler(event) {
var request = event.request;
var uri = request.uri;
if (uri.endsWith("/")) {
request.uri += "index.html";
} else if (!uri.includes(".")) {
request.uri += "/index.html";
}
return request;
}
他のCF2の使用パターン
ドキュメントでは、使用例が豊富でした。
- レスポンスに Cache-Control ヘッダーを追加する
- Cross-Origin Resource Sharing (CORS) ヘッダーをレスポンスに追加
- Cross-Origin Resource Sharing (CORS) ヘッダーをリクエストに追加
- レスポンスにセキュリティヘッダーを追加する
- リクエストに True-Client-IP ヘッダーを追加する
- ビューワーを新しい URL にリダイレクトさせる
- index.html を追加してファイル名を含まない URL をリクエストする
- リクエストの単純なトークンを検証する
CF2のログ出力方法
ログの出力方法は、下記のドキュメントにも記載がありますが、console.log()
を記述し、CloudFrontに設置し、実際に起動させる
必要があります。
L@Eと異なり、ログは、us-east-1
のみで出力・記録されます。
eventの構造
下記で、event構造が記載されています。
例えば、user-agentヘッダーは、request.headers["user-agent"].value
で取得できます。
参考