WebPファイルがある場合、対応ブラウザにはそれを返すWebサーバーの設定方法について書いてきました。
S3に画像ファイルを配置するケースも増えていますが、S3自体には上記の代替となるようなURL書き換えの仕組みがありません。
そこでCloudFrontにリクエストやレスポンスを変換するロジックを実装できる、Lambda@Edgeを使う方法を試してみました。
Lambda@Edge用JavaScriptコード
このようなJavaScriptコードを書きました。
Accept
ヘッダは簡単に取得できます。WebP画像(image.png.webp)の有無は実際にオリジンに対してHEAD
リクエストを送信して確認します。
S3に限らず汎用的なWebサーバをオリジンにできます。
実際に設定するには、CloudFrontの対象Behavior
でLambda Function Associations
にOrigin Request
として対してこのLambda関数を割り当てます。Include Body
は不要です。
Whitelist Headers
にAccept
を追加することも必要です。
const BASE_URL = 'https://s3.amazonaws.com/webp-lambda-edge-test';
const TIMEOUT = 3 * 1000;
const https = require('https'),
url = require('url');
exports.handler = async (event, context) => {
const request = event.Records[0].cf.request;
// JpegまたはPNGファイルへのリクエストのみ対応
if (request.uri.match(/\.(jpe?g|png)$/)) {
// Acceptヘッダがimage/webpを含むか
let webpAcceptable = false;
try {
if (request.headers.accept[0].value.match(/image\/webp/i))
webpAcceptable = true;
} catch (ex) {}
if (webpAcceptable) {
// WebP対応ブラウザの場合はWebP版ファイルの有無をHEADメソッドで確認
await new Promise((resolve, reject) => {
const webpUri = request.uri + '.webp';
const req = url.parse(BASE_URL + webpUri);
req.method = 'HEAD';
req.timeout = TIMEOUT;
https.request(req, res => {
// ステータスコード2XXであればWebPが存在するのでリクエストURIを変更
if (res.statusCode >= 200 && res.statusCode < 300)
request.uri = webpUri;
resolve();
}).on('error', reject).end();
});
}
}
return request;
};
Lambda@Edgeは、通常のLambda関数と異なる点がいくつかあるようです。
設定について詳しい手順もまた今度、書いてみたいと思います。