条件付きリクエストとは?
条件付きリクエストとは、ページが最新かどうかを検証して効率的にキャッシュを使う仕組みです。ページのハッシュ値を使う検証もありますが、今回は簡単のため更新日による検証のみ言及します。あとすごい省略するのでちゃんと知りたい方は途中で紹介するリンク先を参考にしてください。
まず、条件付きリクエストのざっくりした流れは以下です。
ページが更新されていない場合(条件付きリクエストが成功)
- 【クライアント】 あるページのキャッシュが切れたため、オリジンへ「このキャッシュまだ使ってもいい?」と条件付きリクエストを飛ばす
- 【オリジン】 ページが更新されていないため条件付きリクエストが成功し、「まだキャッシュ使っていいよ!」と返信する(このときページのデータを送信しない)
- 【クライアント】 キャッシュをそのまま使い続ける
ページが更新されていた場合(条件付きリクエストが失敗)
- 【クライアント】 あるページのキャッシュが切れたため、オリジンへ「このキャッシュまだ使ってもいい?」と条件付きリクエストを飛ばす
- 【オリジン】 ページが更新されているため条件付きリクエストは失敗し、「キャッシュは使うな!このデータ使え!」と返信する(このときページのデータを送信する)
- 【クライアント】 キャッシュは捨てて新しく受け取ったページのデータを使う
何が嬉しいの?
- ページの更新がない場合はページのデータを送信しないため、通常のリクエストと比較してレスポンスのペイロードが減りページ表示速度が速くなることが期待できる
- パフォーマンス改善のチューニングの中では比較的簡単に対応できる(と思われるため)費用対効果が高い
条件付きリクエストについてもっと詳しく知りたい方は以下を見てください。
条件付きリクエストを使ってみよう
まずは普通にリクエストを投げてヘッダーの値を確認しましょう。
コマンド
curl https://anylog.dev/ --head
レスポンスヘッダー
HTTP/1.1 200 OK
Date: Sun, 19 Feb 2023 16:21:54 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Cache-Control: max-age=600
expires: Sun, 19 Feb 2023 16:31:54 UTC
last-modified: Sun, 19 Feb 2023 07:19:23 GMT
permissions-policy: interest-cohort=()
vary: Origin
CF-Cache-Status: DYNAMIC
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=b7xs3Yblup5dthlUWp7sTO8d8r2N%2BwV359jO46GFJ%2FWhGAf%2BlmPlhdKhia9fv%2FN4Po13aLhcHXc%2Bjh6VqEZ7MRWShxJT%2BZeUOBovooaJKZJcYGvZuEi38z4NqqkZ0ERMPnWvlnW%2Fa6%2Ba"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 79c05692097d202b-NRT
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
ちゃんとレスポンスが 200 で返ってきましたね。ここで重要なのがページの最終更新日時を表す last-modified
です。条件付きリクエストを送るときはこの情報をもとに記事が最新かどうかを問い合わせます。
条件付きリクエストを成功させる
コマンド
curl https://anylog.dev/ --head -H 'if-modified-since: Sun, 19 Feb 2023 07:19:23 GMT'
レスポンスヘッダー
HTTP/1.1 304 Not Modified
Date: Sun, 19 Feb 2023 16:31:51 GMT
Connection: keep-alive
Cache-Control: max-age=600
etag: "e4f5f7c33b8b0828b6109178fbdf3a1aca96b4781b9fbf6aec54c9882996fd0d"
expires: Sun, 19 Feb 2023 16:41:51 UTC
permissions-policy: interest-cohort=()
vary: Origin
CF-Cache-Status: DYNAMIC
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=XVjoGI%2BNd6CCe9Ci9QO0hwJ48PoEE3nanMy6xLRPrDXffYv82QEqemsZQ2D7C0XIQfhL8otdmNBY%2Bfqrn9vUXrgu1V6iQ5QJJ6N%2FzZ%2BGZ7Hfa1aB6uP4R%2FarZ56z9veRT5iAgkTCSx0H"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 79c065253ef980ae-NRT
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
上ではリクエストヘッダー if-modified-since
を追加してリクエストを投げました。これが条件付きリクエストです。そして、if-modified-since
の値を先ほどの last-modified
と同じにしたため、
更新日時が古くない → 記事に更新はない
と判定されて 304 Not Modified
が返ってきてます。
このときのレスポンスボディを以下のコマンドで確認すると
コマンド
curl https://anylog.dev/ -H 'if-modified-since: Sun, 19 Feb 2023 07:19:23 GMT'
レスポンス
空っぽです。今回は curl を使ったためキャッシュは関係ありませんでしたが、ブラウザが条件付きリクエストを送る場合は 304 が返ってくると古いキャッシュをそのまま使う挙動になります。
ブラウザはどうやって条件付きリクエストを送るの?
ブラウザによって挙動が異なるとは思いますが、基本的にオリジンからのレスポンスヘッダーに last-modified
があれば条件付きリクエストを投げると思います。
条件付きリクエストを失敗させる
失敗させるには if-modified-since
に last-modified
より古い日時を入れます。
コマンド
curl https://anylog.dev/ --head -H 'if-modified-since: Sun, 1 Feb 2000 00:00:00 GMT'
レスポンスヘッダー
HTTP/1.1 200 OK
Date: Sun, 19 Feb 2023 16:52:59 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Cache-Control: max-age=600
expires: Sun, 19 Feb 2023 17:02:59 UTC
last-modified: Sun, 19 Feb 2023 07:19:23 GMT
permissions-policy: interest-cohort=()
vary: Origin
CF-Cache-Status: DYNAMIC
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=GCsI2IOHcO6F8XizkFUqP%2Fc32HEOdGXrlvumGLKJyBIIfUYoMPzWxlivfyTH3WLjKCB98zGrLSFlwZOWvOA9pBBaUHx2d3LDKOVhcp6%2FFu7fmL8XYIZjiYuJrULLgzwfCYcq0sbruCq1"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 79c0841b9e97df60-NRT
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
ステータスコードが 200 で返ってきました。つまり、条件付きリクエストの検証が失敗した場合は普通にリクエストを投げた場合と同じになります。ブラウザの挙動としては古いキャッシュを捨てて新しいページのデータを使用します。
どのくらい速くなるの?
このブログのトップページに 100 回リクエストを送って測定します。gzip 圧縮はしません。オリジンサーバは CloudFlare です。
普通にリクエストした場合(=条件付きリクエストの検証が失敗)
Summary:
Total: 4.9565 secs
Slowest: 0.9791 secs
Fastest: 0.2978 secs
Average: 0.3950 secs
Requests/sec: 20.1755
条件付きリクエストが成功した場合
Summary:
Total: 2.4881 secs
Slowest: 0.7057 secs
Fastest: 0.1888 secs
Average: 0.2434 secs
Requests/sec: 40.1914
平均をみると 0.15s 速くなってることがわかります。もっと記事量があるサイトだとさらに差大きくなると思います。
オリジン側の条件付きリクエスト対応
オリジンを何にしてるかによって対応が異なります。S3 や GCS を使ってる場合はデフォで 304 を返してくれるので何も対応しなくて良いですが、自前でサーバを用意してる場合は対応する必要があります。