「Last-Modified」だけじゃ足りない?Webサイトのパフォーマンスを最大化するキャッシュ戦略
HTTP ヘッダーの Last-Modified
は、レスポンスヘッダーの一つで、ウェブサーバーが最後にファイルを更新した日時をクライアントに伝えます。これは、クライアントがキャッシュしたコンテンツが最新かどうかを判断するために使用されます。
仕組み
- ウェブサーバーがファイルを更新すると、
Last-Modified
ヘッダーにそのファイルの更新日時を設定します。 - クライアントがファイルを初めてリクエストすると、サーバーはファイルをレスポンスとして送信し、
Last-Modified
ヘッダーを含めます。 - クライアントはファイルをキャッシュし、
Last-Modified
ヘッダー値を記録します。 - クライアントが次回同じファイルをリクエストすると、
If-Modified-Since
リクエストヘッダーに前回のLast-Modified
ヘッダー値を含めます。 - サーバーは、ファイルの更新日時が
If-Modified-Since
ヘッダー値よりも新しい場合のみ、ファイルを更新バージョンのレスポンスとして送信します。 ファイルが更新されていない場合は、304 "Not Modified" ステータスコードを返し、キャッシュされているファイルをクライアントが引き続き使用することを示します。
利点
- コンテンツの一貫性
クライアントは常に最新のコンテンツを表示できます。 - キャッシュの有効活用
クライアントが毎回同じファイルをダウンロードする必要がなくなり、ネットワーク帯域幅とサーバー負荷を軽減できます。
注意点
- 複数のクライアントがファイルを同時に更新した場合、
Last-Modified
ヘッダーは最後の更新のみを反映するため、データ損失が発生する可能性があります。 Last-Modified
はファイルシステムのタイムスタンプに基づいており、ファイルの内容が実際に変更された日時を正確に反映しているとは限らないことに注意する必要があります。 例えば、ファイルの名前を変更した場合、Last-Modified
ヘッダー値は更新されますが、ファイルの内容は変更されていません。
プログラミングでの実装
Last-Modified
ヘッダーは、ウェブサーバーの設定ファイルで設定できます。 また、多くのプログラミング言語には、Last-Modified
ヘッダーを設定および取得するためのライブラリや関数があります。
- Apache での Last-Modified ヘッダーの設定
Header always set Last-Modified "<the last modified date>"
- Python での Last-Modified ヘッダーの取得
import requests
response = requests.get("https://example.com/")
last_modified = response.headers["Last-Modified"]
print(last_modified)
注記
これらの例はあくまで基本的な使用方法を示すものであり、状況に応じて適宜変更する必要があります。
Python
import requests
# ファイルの更新日時を設定
last_modified_date = datetime.datetime.now()
# リクエストを送信
response = requests.get("https://example.com/", headers={"Last-Modified": last_modified_date.strftime("%a, %d %b %Y %H:%M:%S %Z")})
# レスポンスから Last-Modified ヘッダーを取得
received_last_modified = response.headers["Last-Modified"]
# 取得した Last-Modified ヘッダーを出力
print(received_last_modified)
Java
import java.time.Instant;
import java.net.HttpURLConnection;
import java.net.URL;
public class LastModifiedExample {
public static void main(String[] args) throws Exception {
// ファイルの更新日時を設定
Instant lastModified = Instant.now();
// URL オブジェクトを作成
URL url = new URL("https://example.com/");
// HttpURLConnection オブジェクトを取得
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Last-Modified ヘッダーを設定
connection.setRequestProperty("Last-Modified", lastModified.toString());
// リクエストを送信
connection.connect();
// レスポンスから Last-Modified ヘッダーを取得
String receivedLastModified = connection.getHeaderField("Last-Modified");
// 取得した Last-Modified ヘッダーを出力
System.out.println(receivedLastModified);
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
public class LastModifiedExample {
public static void Main() {
// ファイルの更新日時を設定
DateTime lastModified = DateTime.Now;
// HttpClient オブジェクトを作成
HttpClient client = new HttpClient();
// リクエストヘッダーを作成
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/");
request.Headers.Add("Last-Modified", lastModified.ToString("R"));
// リクエストを送信
HttpResponseMessage response = client.SendAsync(request).Result;
// レスポンスから Last-Modified ヘッダーを取得
string receivedLastModified = response.Headers.GetValues("Last-Modified").First();
// 取得した Last-Modified ヘッダーを出力
Console.WriteLine(receivedLastModified);
}
}
Last-Modified の欠点
- キャッシュの無効化
ファイルが頻繁に変更される場合、Last-Modified
に基づいてキャッシュが無効化される可能性が高くなり、パフォーマンスが低下する可能性があります。 - 同時更新の競合
複数のクライアントがファイルを同時に更新した場合、Last-Modified
ヘッダーは最後の更新のみを反映するため、データ損失が発生する可能性があります。 - 精度が低い
ファイルシステムのタイムスタンプに基づいており、ファイルの内容が実際に変更された日時を正確に反映しているとは限りません。
代替方法
以下の代替方法を検討できます。
- Expires
リソースの有効期限を設定することで、クライアントが期限切れになるまでリソースをキャッシュできるようにします。 - Cache-Control
キャッシュポリシーを指定することで、クライアントがいつリソースを再検証すべきかを制御できます。 - ETag (Entity Tag)
ファイルごとに一意のハッシュ値を生成し、ETag
ヘッダーでクライアントに伝えます。サーバーは、ETag が一致する場合、304 "Not Modified" ステータスコードを返し、キャッシュされたファイルをクライアントが引き続き使用することを示します。
各方法の詳細と比較
方法 | 説明 | 利点 | 欠点 |
---|---|---|---|
Last-Modified | ファイルシステムのタイムスタンプを使用 | シンプルで実装が簡単 | 精度が低い、同時更新の競合、頻繁なキャッシュ無効化 |
ETag | ファイルごとに一意のハッシュ値を使用 | 精度が高く、同時更新の競合を回避できる | Last-Modified より複雑な実装が必要 |
Cache-Control | キャッシュポリシーを指定 | 詳細なキャッシュ制御が可能 | 設定が複雑になる可能性がある |
Expires | リソースの有効期限を設定 | シンプルで実装が簡単 | キャッシュが無効化されるまで古いコンテンツが提供される可能性がある |
適切な方法を選択
使用する代替方法は、アプリケーションのニーズによって異なります。
- 動的コンテンツ または頻繁に変更されるコンテンツの場合は、
Cache-Control
またはExpires
を使用して、キャッシュポリシーをより細かく制御する必要があります。 - 静的コンテンツ の場合は、
ETag
が一般的に良い選択です。