UseOriginCacheControlHeaders マネージドポリシーの Host キャッシュキー指定による挙動を調べてみた

UseOriginCacheControlHeaders マネージドポリシーの Host キャッシュキー指定による挙動を調べてみた

Clock Icon2024.07.10 10:57

いわさです。

先日 CloudFront に新しいマネージドキャッシュポリシーが登場しました。
雑に言うとオリジンの Cache-Control ヘッダーを尊重するよというポリシーです。

https://dev.classmethod.jp/articles/cache-control-cloudfront-managed/

ほーいいじゃないか、こういうのでいいんだよ。と思いながら私も早速使ってみたところ...

% curl https://d1uqy558jl7bhw.cloudfront.net/ -i
HTTP/2 403 
content-type: application/json
content-length: 23
date: Wed, 10 Jul 2024 06:15:08 GMT
x-amzn-requestid: f05dff6b-3778-49dd-989f-ef06f37ad99e
x-amzn-errortype: ForbiddenException
x-amz-apigw-id: arsE-F0DNjMELOQ=
x-cache: Error from cloudfront
via: 1.1 6bdff89d7edf793d60fc3af5190198de.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C2
x-amz-cf-id: vt9mUKe5gvcsw3C8xW9SFX1K4r4L1UcNZEOuqesgwWZsIKTmTILmwA==

{"message":"Forbidden"}

なぜか 403 エラーに。
この時のオリジンは Lambda 関数 URL を使ったのですがメトリクスを見る限りでは関数自体の Invoke がされていないようです。

この挙動を調べるために色々なオリジンタイプを用意して検証してみましたので、その様子を紹介したいと思います。

オリジンタイプを用意

CloudFront はドメイン名からオリジンタイプを自動検出してくれます。
オリジンタイプによって挙動が変わるのか?と思い次のように複数のオリジンをまず用意しました。

60B37042-D5E7-4F51-B717-499B2FE30562_1_105_c

まずは CloudFront を経由せずにオリジンに直接アクセスしてみます。
次のようなイメージです。

9A2B4797-B0E9-4F5F-8FA6-630DBF482C56

試してみたところ当然ながら問題はありませんでした。

# ALB-EC2
% curl hoge-web-alb-1355127211.ap-northeast-1.elb.amazonaws.com -I
HTTP/1.1 200 OK
Date: Wed, 10 Jul 2024 05:43:00 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 5
Connection: keep-alive
Server: Apache/2.4.59 ()
Upgrade: h2,h2c
Last-Modified: Wed, 10 Jul 2024 05:41:30 GMT
ETag: "5-61cde19211f1b"
Accept-Ranges: bytes
Cf-Team: 213cc3b7990000e02170394400000001

# checkip.amazonaws.com
% curl checkip.amazonaws.com -I
HTTP/1.1 200 
Date: Wed, 10 Jul 2024 05:43:10 GMT
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
Connection: keep-alive
Server: nginx
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Cf-Team: 213cc3ddbf0000e021703ea400000001

# hoge0710aaa (API Gateway)
% curl https://7uma0fumhd.execute-api.ap-northeast-1.amazonaws.com/hoge0710/hoge -I
HTTP/2 200 
date: Wed, 10 Jul 2024 05:43:55 GMT
content-type: application/json
content-length: 40
x-amzn-requestid: 7d4cea6d-9ede-44d0-aba2-728a5aa601fb
x-amzn-remapped-content-length: 40
x-amz-apigw-id: arngWGKUtjMEveg=
x-amzn-trace-id: Root=1-668e1f9b-093f5df56250c1ea629d1e6e;Parent=030fd577aab66d85;Sampled=0;lineage=3a6ed96b:0

# lambdaUrl
% curl https://gyduqyxzbykh7bsjmytdgmbmje0tfmpb.lambda-url.ap-northeast-1.on.aws/ -I
HTTP/1.1 200 OK
Date: Wed, 10 Jul 2024 05:44:38 GMT
Content-Type: application/json
Content-Length: 0
Connection: keep-alive
x-amzn-RequestId: c23ff03c-8d9a-41a8-8d26-02d73f6ebeff
X-Amzn-Trace-Id: root=1-668e1fc6-464f21c65d456ec27f55d14d;parent=53939bb02ccbc74e;sampled=0;lineage=3a6ed96b:0

# lightsail (Custom Origin)
% curl http://hoge-iwasa.net/ -I
HTTP/1.1 200 OK
Date: Wed, 10 Jul 2024 05:45:52 GMT
Server: Apache
Last-Modified: Mon, 17 Jun 2024 11:45:43 GMT
ETag: "1303-61b14814f07c0"
Accept-Ranges: bytes
Content-Length: 4867
Vary: Accept-Encoding
Content-Type: text/html
Cf-Team: 213cc657c10000e021709db400000001

# S3 Static WebSite
% curl http://iwasa-hoge-public-bucket.s3-website-ap-northeast-1.amazonaws.com/ -I
HTTP/1.1 200 OK
x-amz-id-2: ccLlRiq++mZMdTOroQfO5Qo3AGV4LoeFPNCG8PM8x/HbWYqQNJhEz7pZEpU5mitCmE+HZ3rpzRM=
x-amz-request-id: 1K1NR9YSC0T15S9G
Date: Wed, 10 Jul 2024 05:46:01 GMT
Last-Modified: Mon, 30 May 2022 23:34:56 GMT
ETag: "2d9191d1c2b2d6b7739df4ca3b818b25"
Content-Type: text/html
Server: AmazonS3
Content-Length: 10
Cf-Team: 213cc676460000e02170a1f400000001

続いてそれぞれのオリジンを CloudFront 経由でアクセスしてみましょう。

3F7295A3-7B0A-434C-B7C5-685DEE0E2952

キャッシュポリシーは今回使いたかった UseOriginCacheControlHeaders を、オリジンリクエストポリシーは AllViewerExceptHostHeader を選択しました。
オリジンリクエストポリシーについては Host ヘッダーさえ転送されなければ今回の検証範囲では不都合は起きないはずです。

E0E3279E-6F56-40DD-91E6-6E3C2C1A99C0

先ほどと同じようにリクエストを送信してみます。

# ALB-EC2
% curl https://d1uqy558jl7bhw.cloudfront.net/ -I
HTTP/2 200 
:

# checkip.amazonaws.com
% curl https://d1uqy558jl7bhw.cloudfront.net/ -I
HTTP/2 200 
:

# API Gateway
% curl https://d1uqy558jl7bhw.cloudfront.net/ -I
HTTP/2 403 
content-type: application/json
content-length: 23
date: Wed, 10 Jul 2024 06:14:23 GMT
x-amzn-requestid: 82bdfae9-11f1-4e09-82cf-8abac4642223
x-amzn-errortype: ForbiddenException
x-amz-apigw-id: arr-BFMoNjMEYog=
x-cache: Error from cloudfront
via: 1.1 4f7d123e12a6d79006c5c9bf3e1ce47a.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C2
x-amz-cf-id: Z_CsFR2oGYyNmsWU5IBiNYlv9dWsiZY6HsXkuWrMBgg4W8aBL3Vo-g==
:

# Lambda Function URL
% curl https://d1uqy558jl7bhw.cloudfront.net/ -I
HTTP/2 403 
content-type: application/json
content-length: 23
date: Wed, 10 Jul 2024 06:15:08 GMT
x-amzn-requestid: f05dff6b-3778-49dd-989f-ef06f37ad99e
x-amzn-errortype: ForbiddenException
x-amz-apigw-id: arsE-F0DNjMELOQ=
x-cache: Error from cloudfront
via: 1.1 6bdff89d7edf793d60fc3af5190198de.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C2
x-amz-cf-id: vt9mUKe5gvcsw3C8xW9SFX1K4r4L1UcNZEOuqesgwWZsIKTmTILmwA==
:

# Custom Origin
% curl https://d1uqy558jl7bhw.cloudfront.net/ -I
HTTP/2 200 
:

# S3 Static Web
% curl https://d1uqy558jl7bhw.cloudfront.net/ -I
HTTP/2 404 
content-type: text/html; charset=utf-8
content-length: 376
date: Wed, 10 Jul 2024 06:21:21 GMT
server: AmazonS3
x-cache: Error from cloudfront
via: 1.1 d4ec4fe8ac7dc1717cdfe6977662568e.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C2
x-amz-cf-id: pbSfeJxRVsbhk_D4LsQWHzvGJ7dDTH9czurKuuYNrZTwMFbk-wYHIg==
age: 1

API Gateway、Lambda 関数 URL、S3 静的ホスティングで問題が生じていますね。
ただ、ELB-EC2 や Lightsail のカスタムオリジンだと問題なく動作しているように見えます。うーん?

ちなみに上記に対して次のように別のマネージドポリシーを設定した場合は問題なく動作しますのでビヘイビアやオリジンの基本設定に問題がある感じではなさそうです。

B4FF88FC-4140-4027-8420-5D4C07891D0E

host ヘッダーが送信されていた

そしてその後調べていて気がついたのですが、S3 静的ホスティングの Body エラーメッセージを確認してみると次のようなレスポンスが取得されていました。

% curl https://d1uqy558jl7bhw.cloudfront.net/
<html>
<head><title>404 Not Found</title></head>
<body>
<h1>404 Not Found</h1>
<ul>
<li>Code: NoSuchBucket</li>
<li>Message: The specified bucket does not exist</li>
<li>BucketName: d1uqy558jl7bhw.cloudfront.net</li>
<li>RequestId: SNQBJN7MA3APA8V8</li>
<li>HostId: lXpeRk6OOiNT9XxuR3UMM2K+06+VT74Nng+ImK9KkkxnEgz5/KwZhp5lE/OIY6hHCrjIlO6Hj34=</li>
</ul>
<hr/>
</body>
</html>

バケットが存在しないと言われていますね。そしてバケット名が CloudFront ディストリビューションのドメインです。これか。

ここでようやく UseOriginCacheControlHeaders マネージドキャッシュポリシーのキャッシュキーに host が含まれていることに気が付きました。
キャッシュポリシーのキャッシュキーは自動でオリジンリクエストに追加されるので、AllViewerExceptHostHeader ポリシーを使ってもビューアの Host ヘッダーが転送されるというわけですね。なんてこったい。

4C9EF478-7ECE-4D05-9875-A429F6DBA159

結局カスタムキャッシュポリシーを作成しました

で、このマネージドポリシーは編集出来そうに見えるのですが...

E241F54A-6C0A-4841-98C1-EE63C09D6709

やってみると変更は許可されていないとエラーメッセージが表示されます。

95C2D349-7EA3-4BEA-A651-E45B108EC74C_4_5005_c

というわけでカスタムキャッシュポリシーを作りました。
内容は同じで、キャッシュキーから host ヘッダーだけ除きました。

EBCCBC87-7449-4F23-93EB-3088CCEF0EB8

このカスタムキャッシュポリシーを設定すると期待どおり動作しました。

F4BDAE44-5CB4-4A8E-93BC-61E14A4060CB

Cache-Control ヘッダーの設定に準拠して CloudFront のキャッシュをコントロール出来るようになりました。
ちなみに Lambda 関数 URL では次のようにmax-ageで調整したり試してみました。

export const handler = async (event) => {
  const response = {
    statusCode: 200,
    headers: {
      'Cache-Control': 'public, max-age=5'
    },
    body: JSON.stringify({timestamp: new Date()}),
  };
  return response;
};
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:18.225Z"}
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:18.225Z"}
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:23.149Z"}
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:23.149Z"}
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:23.149Z"}
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:28.547Z"}
% curl https://d1uqy558jl7bhw.cloudfront.net/
{"timestamp":"2024-07-10T08:16:28.547Z"}

なお、Cache-Control ヘッダーの仕様についてこちらを参考にさせて頂いております。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control

さいごに

本日は UseOriginCacheControlHeaders マネージドポリシーの Host キャッシュキー指定による挙動を調べてみました。

キャッシュキーの host が必要なのか?はちょっと疑問なのですが、このケースの host ヘッダー転送は期待した挙動にならないことが多い気もするので、修正されるような気もしています。
いずれにせよマネージドポリシーが期待どおり動作しない場合もカスタムポリシーを作ることで回避出来ることが多いので覚えておきましょう。

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.