[小ネタ] CloudFrontでクエリ文字列を転送しているときのInvalidationにはクエリ文字列を忘れないように注意しよう

Amazon CloudFrontでクエリ文字列をオリジンに転送するよう設定していてキャッシュクリアを行う場合には、Invalidationのパス指定の際にクエリ文字列部分も考慮しないと、クエリ文字列を含んだリクエストについてはキャッシュが無効化されません。
2020.05.31

はじめに

清水です。タイトルのとおりなのですが、Amazon CloudFrontでクエリ文字列をオリジンに転送するよう設定している場合でInvalidation(ファイルの無効化)を行う際には、Invalidation対象のパスにクエリ文字列を含める必要があります。(クエリ文字列部分のワイルドカードの利用も可です。)Cookieやヘッダーをオリジンに転送している場合は対象となるパスのみを指定すれば良いため、混乱することがあるかもしれません。というか私が混乱してしまったので備忘録がてらまとめてみたいと思います。

CloudFrontでクエリ文字列をオリジンに転送している際のInvalidationについて確認してみた

Amazon CloudFront開発者ガイドを確認すると、その旨記載があります。

以下では実際に、オリジンにクエリ文字列を転送するCloudFrontを構築、Invalidation時の挙動を確認してみました。

確認する環境について

オリジンはEC2にApache+PHPをインストールし、リクエスト時の時間とクエリ文字列を出力するコード(qs.php)を配置しました。コードは以下になります。

qs.php

<?php
date_default_timezone_set('Asia/Tokyo');
echo date("Y/m/d H:i:s");
echo "\n";
echo $_SERVER['QUERY_STRING']
?>

実際にアクセスすると、以下のような出力となります。

$ curl "http://ec2-XXX-XXX-XXX-XXX.ap-northeast-1.compute.amazonaws.com/qs.php?foo=bar"
2020/05/31 10:55:48
foo=bar

このEC2をオリジンとして、CloudFrontディストリビューションを作成します。クエリ文字列をすべてオリジンに転送するよう設定し、またキャッシュTTLは1日(86400秒)となるよう設定します。

クエリ文字列含め、キャッシュされていることを確認する

上記の環境で、例えば以下のようにdateコマンド後にcurlコマンドを実行し、リクエスト時の時刻とレスポンスの内容に含まれる時刻が同じであれば、キャッシュが使われなかった状態(キャッシュミス)、異なる時間であればキャッシュが使われた状態(キャッシュヒット)状態、ということがわかります。

キャッシュが使われなかった状態

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 10時59分25秒 JST
2020/05/31 10:59:25
foo=bar

キャッシュが使われた状態

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 11時05分05秒 JST
2020/05/31 10:59:25
foo=bar

Invalidation(ファイルを無効化/キャッシュクリア)した場合、意図した通りにできてれば、次のアクセスではキャッシュが使われなかった状態になり、リクエスト時の時刻とレスポンスの内容に含まれる時刻が同じになるはずです。(コンマ数秒の時差で1秒は違いが出るかもしれませんが。)

準備として、いくつかのクエリ文字列をキャッシュさせておく

まずは準備として、いくつかのクエリ文字列に対して、以降のアクセスでキャッシュを使うよう、あらかじめアクセスしてキャッシュさせておきます。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 10時59分25秒 JST
2020/05/31 10:59:25
foo=bar

 $ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=baz"
2020年 5月31日 日曜日 10時59分39秒 JST
2020/05/31 10:59:39
foo=baz

 $ date; curl "https://d1234567890.cloudfront.net/qs.php"
2020年 5月31日 日曜日 11時06分09秒 JST
2020/05/31 11:06:09

 $ date; curl "https://d1234567890.cloudfront.net/qs.php?hoovers=ooover"
2020年 5月31日 日曜日 11時06分59秒 JST
2020/05/31 11:06:59
hoovers=ooover

クエリ文字列なしでInvalidationしてみる

まずはクエリ文字列なしでInvalidationしてみます。/qs.phpでInvalidationを実行します。

Invalidation完了後、確認してみましょう。クエリ文字列なしの場合は以下のようにキャッシュがクリアされました。(アクセス時にキャッシュが使われませんでした。)

$ date; curl "https://d1234567890.cloudfront.net/qs.php"
2020年 5月31日 日曜日 11時16分43秒 JST
2020/05/31 11:16:43

しかしクエリ文字列をつけたアクセスでは、下記のようにキャッシュが使われた状態となりました。ファイル無効化(Invalidation)の際はクエリ文字列を含める必要があるので、クエリ文字列を含めない場合は、クエリ文字列なしの場合しかInvalidationが行われない、ということですね。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 11時24分36秒 JST
2020/05/31 10:59:25
foo=bar

 $ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=baz"
2020年 5月31日 日曜日 11時24分39秒 JST
2020/05/31 10:59:39
foo=baz

クエリ文字列を指定してInvalidationしてみる

続いてクエリ文字列部分を指定してInvalidationをしてみましょう。/qs.php?foo=barでInvalidationを実行します。

Invalidation完了後、確認します。以下のようにInvalidation対象としたクエリ文字列についてはキャッシュクリアされていることが確認できます。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 11時27分37秒 JST
2020/05/31 11:27:37
foo=bar

クエリ文字列のうち、そのkeyは同じでもvalue部分が異なるリクエスト、/qs.php?foo=bazについてはInvalidation対象でなかったため、キャッシュクリアされておらず、キャッシュが利用されている状態です。

[shimizu.toshiya@classmethod] [~]
 $ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=baz"
2020年 5月31日 日曜日 11時28分45秒 JST
2020/05/31 10:59:39
foo=baz

keyもvalueも異なるリクエストについても同様、キャッシュクリアされていません。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?hoovers=ooover"
2020年 5月31日 日曜日 11時29分47秒 JST
2020/05/31 11:06:59
hoovers=ooover

クエリ文字列なしのリクエストについても、同じくキャッシュクリアされていません。

$ date; curl "https://d1234567890.cloudfront.net/qs.php"
2020年 5月31日 日曜日 11時30分16秒 JST
2020/05/31 11:16:43

クエリ文字列にワイルドカード「*」を使ってInvalidationしてみる

クエリ文字列を含めたInvalidationを行う際に、そのクエリ文字列部分にもワイルドカードを利用することができます。/qs.php?fo*でInvalidationしてみます。

foから始まるクエリ文字列がキャッシュクリアされました。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 11時36分49秒 JST
2020/05/31 11:36:49
foo=bar

 $ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=baz"
2020年 5月31日 日曜日 11時36分53秒 JST
2020/05/31 11:36:53
foo=baz

もちろん、クエリ文字列部分がfo*にマッチしないパスについては、キャッシュクリアされていません。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?hoovers=ooover"
2020年 5月31日 日曜日 11時38分09秒 JST
2020/05/31 11:06:59
hoovers=ooover

 $ date; curl "https://d1234567890.cloudfront.net/qs.php"
2020年 5月31日 日曜日 11時38分12秒 JST
2020/05/31 11:16:43

ファイル名+ワイルドカード「*」でInvalidationしてみる

同じファイル(今回ならqs.php)に対するすべてのクエリ文字列に対してInvalidationを行いたい場合には、qs.php*のようにファイル名の後にワイルドカード*を付与することで実現できます。

Invalidation完了後、確認してみると以下のように、qs.phpへのアクセスでクエリ文字列に関わらずキャッシュクリアされていることがわかります。

$ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=bar"
2020年 5月31日 日曜日 11時41分56秒 JST
2020/05/31 11:41:56
foo=bar

 $ date; curl "https://d1234567890.cloudfront.net/qs.php?foo=baz"
2020年 5月31日 日曜日 11時42分06秒 JST
2020/05/31 11:42:06
foo=baz

 $ date; curl "https://d1234567890.cloudfront.net/qs.php?hoovers=ooover"
2020年 5月31日 日曜日 11時42分12秒 JST
2020/05/31 11:42:12
hoovers=ooover

 $ date; curl "https://d1234567890.cloudfront.net/qs.php"
2020年 5月31日 日曜日 11時42分18秒 JST
2020/05/31 11:42:18

まとめ

Amazon CloudFrontでクエリ文字列オリジンに転送しているよう設定している場合、Invalidation(ファイル無効化)の際にはクエリ文字列部分も含めて指定が必要であることを確認してみました。オリジンへの転送項目となる他の2項目、Cookieとヘッダーについては対象パスについて一括でInvalidationされるので私は少しこんがらがってしまっていましたが、クエリ文字列についてはURLの一部でもあるので、ブラウザなどで確認できるURLをInvalidationの際にパスとして指定する、と把握しておけば忘れにくいかもしれません。