「Last-Modified」だけじゃ足りない?Webサイトのパフォーマンスを最大化するキャッシュ戦略


HTTP ヘッダーの Last-Modified は、レスポンスヘッダーの一つで、ウェブサーバーが最後にファイルを更新した日時をクライアントに伝えます。これは、クライアントがキャッシュしたコンテンツが最新かどうかを判断するために使用されます。

仕組み

  1. ウェブサーバーがファイルを更新すると、Last-Modified ヘッダーにそのファイルの更新日時を設定します。
  2. クライアントがファイルを初めてリクエストすると、サーバーはファイルをレスポンスとして送信し、Last-Modified ヘッダーを含めます。
  3. クライアントはファイルをキャッシュし、Last-Modified ヘッダー値を記録します。
  4. クライアントが次回同じファイルをリクエストすると、If-Modified-Since リクエストヘッダーに前回の Last-Modified ヘッダー値を含めます。
  5. サーバーは、ファイルの更新日時が 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 が一般的に良い選択です。