CloudFrontとS3を使ったWebサイトでWebPの画像を配信してみた
WebPに対応している場合はWebPを配信したい
こんにちは、のんピ(@non____97)です。
皆さんはCloudFrontとS3を使ったWebサイトで、クライアントがWebPに対応している場合はWebPを配信したいなと思ったことはありますか? 私はあります。
JPEGやPNGではなく、WebPを使用した方が配信速度の向上が見られるなどメリットを享受することが可能です。
ただし、IEや古いSafari、Firefoxなどごくごく稀にWebPに対応していないことがあります。
そのような場合はWebPではなく、JPEGを配信したいところです。
WebPをサポートしているかはHTTPリクエストヘッダーのAccept
にimage/webp
を含んでいるかで判断できます。2025/1/24時点ではS3単体でリクエストヘッダーをベースにレスポンスするオブジェクトを切り替えることはできません。
ということで、CloudFront Functions、もしくはLambda@EdgeでAccept
ヘッダーをベースに動的にレスポンスする画像ファイルを切り替えてみました。
いきなりまとめ
- 動的にWebPをレスポンスするか判定するためには
Accept
ヘッダーおよび CloudFront Functions or Lambda@Edgeを使おう- CloudFront Functionsの場合は、全ての画像ファイルがWebP形式に対応していることが前提
- 対応していない場合は404が返る
- Lambda@Edgeの場合は、オブジェクトが存在するか判定する時間的余裕があるため、
.webp
が存在しなければクライアントのオリジナルのURIのファイルを返す
- CloudFront Functionsの場合は、全ての画像ファイルがWebP形式に対応していることが前提
やってみた
検証環境
検証環境は以下のとおりです。
※ 本当はAthenaのクエリ出力先S3バケットやGlueテーブルなどもありますが、割愛
検証環境は全てAWS CDKでデプロイしました。使用したコードは以下GitHubリポジトリに保存しています。
こちらのベースとなったコードの詳細な説明は以下記事をご覧ください。
以降、これらの記事で紹介しているコードからの主な変更点を紹介します。
コンテンツのデプロイ
まずはコンテンツのデプロイです。
コンテンツのデプロイはWebPかそれ以外かの2回に分けて行っています。
// Deploy contents
if (!props.contentsPath) {
return;
}
const asset = cdk.aws_s3_deployment.Source.asset(props.contentsPath, {
exclude: [".DS_Store"],
});
new cdk.aws_s3_deployment.BucketDeployment(this, "DeployContents", {
sources: [asset],
destinationBucket: props.websiteBucketConstruct.bucket,
exclude: ["*.webp"],
distribution,
distributionPaths: ["/*"],
});
new cdk.aws_s3_deployment.BucketDeployment(this, "DeployContentsWebP", {
sources: [asset],
destinationBucket: props.websiteBucketConstruct.bucket,
exclude: ["*"],
include: ["*.webp"],
distribution,
distributionPaths: ["/*"],
contentType: "image/webp",
});
というのもaws_s3_deployment.BucketDeploymentで、まとめて全てデプロイすると、WebPのオブジェクトのcontentType
がbinary/octet-stream
となってしまいます。
binary/octet-stream
の場合、ブラウザ上に画面は表示されず、画像ファイルをダウンロードする形になってしまいます。
そのため、対応として拡張子がwebp
のファイルについては明示的にcontentType
でimage/webp
を指定しています。
ちなみに今回WebPファイルの生成にはcwebpを使用しました。
> cwebp non__97.png -o non__97.png.webp
Saving file 'non__97.png.webp'
File: non__97.png
Dimension: 421 x 440
Output: 5464 bytes Y-U-V-All-PSNR 44.77 49.16 50.29 45.88 dB
(0.24 bpp)
block count: intra4: 166 (21.96%)
intra16: 590 (78.04%)
skipped: 560 (74.07%)
bytes used: header: 96 (1.8%)
mode-partition: 905 (16.6%)
Residuals bytes |segment 1|segment 2|segment 3|segment 4| total
macroblocks: | 1%| 7%| 15%| 78%| 756
quantizer: | 36 | 36 | 31 | 24 |
filter level: | 11 | 8 | 5 | 4 |
> ls -lh | grep -e png -e jpeg
-rw-r--r--@ 1 <ユーザー名> <グループ名> 34K 10 11 2016 non__97.jpeg
-rw-r--r--@ 1 <ユーザー名> <グループ名> 75K 7 2 2024 non__97.png
-rw-r--r--@ 1 <ユーザー名> <グループ名> 5.3K 1 24 10:52 non__97.png.webp
-rw-r--r--@ 1 <ユーザー名> <グループ名> 125K 1 24 10:49 non__97_2.png
キャッシュポリシー
Accept
ヘッダーごとにキャッシュするようにキャッシュポリシーを設定しました。
const acceptCachePolicy = new cdk.aws_cloudfront.CachePolicy(
this,
"AcceptCachePolicy",
{
defaultTtl: cdk.Duration.days(1),
minTtl: cdk.Duration.seconds(1),
maxTtl: cdk.Duration.days(7),
cookieBehavior: cdk.aws_cloudfront.CacheCookieBehavior.none(),
headerBehavior:
cdk.aws_cloudfront.CacheHeaderBehavior.allowList("Accept"),
queryStringBehavior: cdk.aws_cloudfront.CacheQueryStringBehavior.none(),
enableAcceptEncodingGzip: true,
enableAcceptEncodingBrotli: true,
}
);
Accept
ヘッダーごとにキャッシュしなければ、WebPをサポートしていないクライアントがアクセスしてきた場合にもWebPで返してしまいます。
また、用意したキャッシュポリシーを適応するのはJPEGやPNGのみにしています。
const addBehaviorOptions: cdk.aws_cloudfront.AddBehaviorOptions = {
allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cdk.aws_cloudfront.CachedMethods.CACHE_GET_HEAD,
cachePolicy: acceptCachePolicy,
originRequestPolicy:
cdk.aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
viewerProtocolPolicy:
cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
responseHeadersPolicy:
cdk.aws_cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS,
functionAssociations: rewriteToWebpCF2
? [
{
function: rewriteToWebpCF2,
eventType: cdk.aws_cloudfront.FunctionEventType.VIEWER_REQUEST,
},
]
: undefined,
edgeLambdas: rewriteToWebpLambdaEdge
? [
{
functionVersion: rewriteToWebpLambdaEdge.currentVersion,
eventType: cdk.aws_cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
},
]
: undefined,
};
distribution.addBehavior(
"/*.jpe?g",
cdk.aws_cloudfront_origins.S3BucketOrigin.withOriginAccessControl(
props.websiteBucketConstruct.bucket
),
addBehaviorOptions
);
distribution.addBehavior(
"/*.png",
cdk.aws_cloudfront_origins.S3BucketOrigin.withOriginAccessControl(
props.websiteBucketConstruct.bucket
),
addBehaviorOptions
);
注意点として、Accept
ヘッダーはブラウザやアクセスの仕方によって大きく異なるため、キャッシュヒット率が低下します。
例えば、私のChromeの場合はimage/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
やtext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
で、Safariはimage/webp,image/avif,image/jxl,image/heic,image/heic-sequence,video/*;q=0.8,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5
です。
もし、キャッシュヒット率を下げたくないのであれば、Viewer RequestでCloudFront Functionsを動かし、Accept
ヘッダー内にimage/webp
を含んでいる場合はx-available-webp
などとカスタムヘッダーを付与した上で、URIの末尾に.webp
を付与してリクエストするといった対応が必要になります。
CloudFront Functionsで判定する場合
CloudFront Functionsで判定する場合について紹介します。
行っていることは以下のとおりです。
Accept
ヘッダー内にimage/webp
を含む かつ 拡張子がjpeg or jpg or pngの場合、URIの末尾に.webp
を付与してリクエストするAccept
ヘッダー内にimage/webp
を含まない かつ.webp
にアクセスしようとしている場合は末尾の.webp
を削除してリクエストする
async function handler(event) {
const request = event.request;
const uri = request.uri;
// Check WebP support in Accept header
const headers = request.headers;
const acceptHeader = headers.accept ? headers.accept.value : '';
const supportsWebP = acceptHeader.includes('image/webp');
// Regular expression for image extensions
const imageExtRegex = /\.(jpe?g|png)$/i;
if (supportsWebP && imageExtRegex.test(uri)) {
// Add custom header to track WebP conversion attempt
request.headers['x-original-uri'] = {
value: uri
};
// Append .webp to the URI for supported browsers
request.uri = `${uri}.webp`;
}
// Handle .webp requests for non-supporting browsers
else if (uri.endsWith('.webp') && !supportsWebP) {
// Remove .webp extension to get original image
request.uri = uri.slice(0, -5);
}
return request;
}
実際に挙動を確認してみましょう。
まずはAccept
ヘッダーにimage/webp
を指定せずにnon__97.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 05:22:37 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 23bc6d6a912d17773e1bf97197cbfc1e.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: ddWdCadlo-Lj8rrv32SNK6u6RPoUgnRTbN9MAjHm9rptyna6JVyunA==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 05:22:37 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 9b8a6e30994167e8de984036681d4ff6.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: UsFAHVde0acvEnQVSK9yEi_7aj5nUHEJBR_cIoPeAh3bUU2gAckBbg==
age: 4
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
ステータスコードが200でcontent-type
がimage/png
と、正常にPNGファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、Accept
ヘッダーにimage/webp
を指定してnon__97.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Fri, 24 Jan 2025 05:23:18 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 d8a0cc77a7428fd572abace71c0eeda2.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: DDhAyxXtrdajsW6tzfUWybww3leTKQ0ohF6QBvvQt1ZMwuj1-H6APA==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Fri, 24 Jan 2025 05:23:18 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 269160a4d1e0a4937fee2132fea7cb32.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: Ftx5QMM7YcnwcY3ULkEf314YhfhvgJFS-nEpiFSRYfyUThoyCRVRRg==
age: 3
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
ステータスコードが200でcontent-type
がimage/webp
と、正常にWebPファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、続いて、Accept
ヘッダーにimage/webp
を指定してnon__97_2.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97_2.png -H "Accept:image/webp"
HTTP/2 404
content-type: text/html
content-length: 12
date: Fri, 24 Jan 2025 06:56:18 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "347dfa37997b9353b3da6992f8753439"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Error from cloudfront
via: 1.1 b4fcd16c2d55faa87f8fa28379c19ab0.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: qgYXjKNuYUtHe8dTr9CwZoWNHl_wp17iOZSPoTFZpIchYo9f9783Dw==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
はい、404エラーになりました。なぜならnon__97_2.png.webp
というオブジェクトがS3バケット上に存在しないためです。
CloudFront Functionsの最大実行時間は1msです。
そのため、オブジェクトにHEADでアクセスして、実際に存在するのか判定する時間的余裕はありません。
ということで、CloudFront FunctionsでWebPの判定をする場合は、JPEGやPNGに対応してWebPファイルが必ず存在している必要があります。
Lambda@Edgeで判定する場合
Lambda@Edge版も紹介します。
Lambda@Edgeでは以下のような処理を行っています。
Accept
ヘッダー内にimage/webp
を含む かつ 拡張子がjpeg or jpg or pngの場合- URIの末尾に
.webp
を付与した上で、HEADメソッドでリクエストを行い、S3バケット上に.webp
のオブジェクトが存在するか判定する- 存在する場合はオリジンにリクエストする
- 存在しない場合は元のURIでリクエストする
- URIの末尾に
Lambda@EdgeはOrigin Requestの場合タイムアウトは最大30秒です。
そのため、オリジンに実際にリクエストを投げる前にHEADをして、オブジェクトが存在するかどうかの判定を行うことが可能です。
また、Lambda@EdgeはOrigin Requestでも動作させることが可能なので、キャッシュが効いている場合は実行されないのも嬉しいポイントです。
実際のコードは以下のとおりです。
import { CloudFrontRequestEvent, CloudFrontRequest } from "aws-lambda";
import * as https from "https";
// Constants
const TIMEOUT_MS = 2000;
const IMAGE_EXTENSION_PATTERN = /\.(jpe?g|png)$/i;
// Types
type WebPCheckResult = {
availableWebP: boolean;
error?: Error;
};
/**
* Lambda@Edge handler for WebP image conversion
*/
export const handler = async (event: CloudFrontRequestEvent) => {
const request = event.Records[0].cf.request;
const uri = request.uri;
// Check WebP support using the same logic as CloudFront Functions
const acceptHeader = request.headers.accept?.[0]?.value ?? "";
const supportsWebP = acceptHeader.includes("image/webp");
// Process if the request is for an image and browser supports WebP
if (supportsWebP && IMAGE_EXTENSION_PATTERN.test(uri)) {
const webpCheckResult = await checkWebpAvailability(request);
if (webpCheckResult.availableWebP) {
// Store original URI in header
request.headers["x-original-uri"] = [
{
key: "x-original-uri",
value: uri,
},
];
request.uri = `${uri}.webp`;
}
}
return request;
};
/**
* Check if WebP version of the image is available
*/
async function checkWebpAvailability(
request: CloudFrontRequest
): Promise<WebPCheckResult> {
if (!request.origin?.s3?.domainName) {
return { availableWebP: false };
}
try {
const exists = await objectExists(
request.origin.s3.domainName,
`${request.uri}.webp`
);
return { availableWebP: exists };
} catch (error) {
console.error("Error checking WebP existence:", error);
return {
availableWebP: false,
error: error instanceof Error ? error : new Error("Unknown error"),
};
}
}
/**
* Check if object exists in S3 bucket
*/
function objectExists(domainName: string, path: string): Promise<boolean> {
return new Promise((resolve, reject) => {
const request = https.request(
{
hostname: domainName,
path: path,
method: "HEAD",
timeout: TIMEOUT_MS,
},
(response) => {
if (response.statusCode === undefined) {
reject(new Error("Status code is undefined"));
return;
}
resolve(response.statusCode >= 200 && response.statusCode < 300);
}
);
request.on("error", reject);
request.on("timeout", () => {
request.destroy();
reject(new Error(`Request timeout after ${TIMEOUT_MS}ms`));
});
request.end();
});
}
挙動の確認をしましょう。
まずはAccept
ヘッダーにimage/webp
を指定せずにnon__97.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 07:43:00 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 f22f45735eceb3450fbe806ce121aab8.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: s2slRpGdQGob3x0Xz83e9B6Bi_E51mhD352c2hmEaN5z3q-LSKgZFg==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 07:43:00 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 f0499023f5cce9a24cc0ed91910c47ee.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: hJjV-sHPgxsltc8B8oD7SLtCk8qK9BQcQpKbVve3Qgb65SNDiXr6sQ==
age: 7
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
ステータスコードが200でcontent-type
がimage/png
と、正常にPNGファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、Accept
ヘッダーにimage/webp
を指定してnon__97.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 07:43:51 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 03e670dad9bf75ede7f4618a9edd6fde.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: ns8zyzyJctO3GUycsi92WRiAnEuQ0v6GfBKoDSj0P-0niB8fu6u-FA==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 07:43:51 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 d6b84a5611c3f3ea786cd180e1d7ebee.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: 7xHsyUJ4h8_yOa4RDobHpy2zF17lrrc2ciSpIaT4Ot1PL4SynhgCzg==
age: 5
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
ステータスコードが200でcontent-type
がimage/webp
と、正常にWebPファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、続いて、Accept
ヘッダーにimage/webp
を指定してnon__97_2.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97_2.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Fri, 24 Jan 2025 07:45:34 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 67c8b7e623dc98088ceb29dc1e64b5ea.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: es7owe0qum0lq2q6HaOEpVYF9sIV8i947nSOzWX7_kgRkhz1W_Rq0A==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97_2.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Fri, 24 Jan 2025 07:45:34 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 f790dd98745df719189c547ecb87d18e.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: S-DelpS_4zo6HBpMOH8zpDlyjNRKmnPrD8SrlXlEDh0-HejfJ8GkfQ==
age: 2
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
Accept:image/webp
とヘッダーを指定しましたが、non__97_2.png.webp
というオブジェクトは存在しないため、non__97_2.png
にアクセスしcontent-type: image/png
が返ってきました。2回目のアクセス数ではキャッシュが効いていることも確認できます。
おまけで、Accept
ヘッダーにtext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
を指定してnon__97_2.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97_2.png -H "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Fri, 24 Jan 2025 07:49:59 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 9edec502e732ce2bc0b08066a0b40af4.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: ux7xVQZ6oG6e3iWElYszd_MImSmIxfWVa4hKyhQsi6lUnN3lb_SORg==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97_2.png -H "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Fri, 24 Jan 2025 07:49:59 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 8d094829a2df82945a7c7fbea18cea10.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: 0s1zcQM-Mhyrnr9zr54v6pU4QE1fXEn6QFH0WdBAno24g0xkPXuThw==
age: 24
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
はい、問題なく動作しています。
CloudFrontとS3でWebサイトを構成している場合、Acceptヘッダーに応じて動的にWebPを配信するのはひと工夫が必要
CloudFrontとS3を使ったWebサイトでWebPの画像を配信してみました。
CloudFrontとS3でWebサイトを構成している場合、Acceptヘッダーに応じて動的にWebPを配信するのはひと工夫が必要です。
個人的にはLambda@Edgeで対応するのが良いのかなと考えています。
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!