HTTPキャッシュの基礎と仕組み


[![Group181.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/2afa7a01-af42-4eda-9d52-ad8accbaed6e.png)](https://leapcell.io/?lc_t=d_cache)


**[Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/?lc_t=d_cache)**

# HTTPキャッシュメカニズムの詳細解説
ネットワークアプリケーション開発において、ウェブサイトのアクセス速度と効率を向上させることは非常に重要です。様々な種類のキャッシュを設計することで、不要なデータの伝送とリクエストを効果的に避けることができ、ウェブサイトの応答速度を大幅に加速することができます。HTTPプロトコル自体には組み込まれたHTTPキャッシュメカニズムがあります。この記事では、HTTPキャッシュメカニズムとその応用について詳しく掘り下げて説明します。

## HTTPにおけるキャッシュの種類
キャッシュの原理は、リクエストされたリソースのコピーをローカルに保存し、再度同じリクエストがあったときに、サーバーから再度ダウンロードする代わりに、そのコピーを直接返すことで、リソースの伝送を減らし、効率を向上させることです。HTTPキャッシュは、直接リソースをアクセスして返すこと以外に、主に2つのカテゴリに分けられます：
1. **共有キャッシュ**：異なるクライアントが共有キャッシュからリソースを取得できます。これは、複数のクライアントが同じリソースにアクセスするシナリオに適しています。Webプロキシサーバーで一般的に適用されます。多くのユーザーがサーバー上のリソースコピーを共有することができ、各ユーザーが繰り返し保存することを避け、リソースの無駄なコピーを減らすことができます。
2. **個別キャッシュ**：特定のユーザーまたはクライアントのみがアクセスでき、他のユーザーにはアクセス権がありません。ブラウザキャッシュは通常、個別キャッシュに属しており、現在のブラウザにのみサービスを提供し、他のブラウザと共有されることはありません。

## HTTPにおけるキャッシュ応答のステータス
HTTPキャッシュは一般的にGETリクエストに適用されます。なぜなら、GETリクエストは通常、URI以外に他のパラメータを持たず、主にサーバーからリソースを取得するために使用されるためです。異なるGETリクエストは異なるステータスコードを返します：
- **200 OK**：リソースが正常に返されます。
- **301**：リダイレクトです。
- **404**：リクエストされたリソースが存在せず、例外が発生します。
- **206**：不完全な結果が返されます。

## HTTPにおけるキャッシュ制御
HTTPキャッシュ制御は、HTTPヘッダを通じて実装されます。HTTP 1.1では、Cache-Controlが導入され、これによりリクエストと応答のキャッシュ動作を細かく制御することができます：
- **キャッシュしない**：`Cache-Control: no-store`を使用すると、リソースがキャッシュされないことが保証されます。
- **キャッシュの検証**：`Cache-Control: no-cache`は、クライアントキャッシュの検証が必要です。
- **強制検証**：`Cache-Control: must-revalidate`では、有効期限切れのリソースは使用されません。
- **キャッシュスコープ制御**：サーバーは`Cache-Control: private`または`Cache-Control: public`を使用して、キャッシュが個別か共有かを制御することができます。
- **有効期限の設定**：`Cache-Control: max-age=31536000`は、リソースの有効時間を設定し、Expiresヘッダを上書きします。この時間内であれば、リソースは最新であるとみなされ、サーバーから再取得する必要はありません。

HTTP 1.0では、Pragmaフィールドを使用して同様の機能を実現することができます。例えば、`Pragma: no-cache`は`Cache-Control: no-cache`と似た効果を持ち、クライアントにキャッシュをサーバーに提出して検証するよう強制します。ただし、サーバーの応答には通常Pragmaが含まれないため、Pragmaは完全にCache-Controlを置き換えることはできません。

## キャッシュの更新
クライアントがリソースをキャッシュした後、セキュリティの理由から、有効期限を設定する必要があります。有効期限内であれば、キャッシュは有効です。有効期限を超えると、リソースをサーバーから再取得する必要があります。このメカニズムにより、クライアントが取得するリソースが常に最新であり、サーバー側の更新がクライアントにタイムリーに同期されることが保証されます。
- **キャッシュの状態**：クライアントのリソースが有効期限内であれば、状態は新鮮（fresh）です。それ以外の場合は古い（stale）です。
- **有効期限切れの処理**：リソースが古い状態になったとき、クライアントから即座に削除されるわけではありません。代わりに、次のリクエストで`If-None-Match`リクエストをサーバーに送信し、サーバー側のリソースがまだ新鮮かどうかを判断します。リソースが変更されていなければ、サーバーは304（Not Modified）を返し、リソースがまだ有効であることを示します。
- **新鮮度の計算**：リソースの新鮮期間は`Cache-Control: max-age=N`によって決定されます。応答にこのヘッダフィールドが存在しない場合は、Expiresヘッダが存在するかどうかをチェックします。存在する場合は、新鮮期間は`Expires - Date`です。どちらも存在しない場合は、Last-Modifiedヘッダを探し、新鮮期間は`（Date - Last-modified ）/ 10`です。

## バージョン更新（Revving）
HTTPリクエストの効率を向上させるために、一般的にキャッシュ期間を長くすることが望ましいです。しかし、キャッシュ期間が長すぎると、サーバーリソースの更新が困難になります。頻繁に更新されないファイルの場合、ファイル名とバージョン番号をリクエストURLに追加することができます。同じバージョン番号は、リソースの内容が変更されていないことを示し、長期間キャッシュすることができます。サーバーリソースの内容が変更されたとき、リクエストURL内のバージョン番号を更新するだけです。現代のフロントエンドパッケージングツールを利用することで、この操作を実装するのは難しくありません。

## キャッシュの検証
キャッシュされたリソースが有効期限切れになったとき、2つの方法で対処することができます：サーバーから再度リソースをリクエストするか、キャッシュされたリソースを再検証するかです。再検証にはサーバーのサポートが必要であり、`Cache-Control: must-revalidate`リクエストヘッダを設定する必要があります。
- **検証方法**：クライアントがリソースの有効性を検証するとき、直接リソースをサーバーに送信することはできません。そうすると、リソースの浪費につながります。HTTPはETagsヘッダを提供し、これをリソースの一意の識別子として使用します。クライアントは`If-None-Match`リクエストを送信し、サーバーにリソースが一致するかどうかを判断させます。これは強い検証です。応答にLast-Modifiedが含まれている場合、クライアントは`If-Modified-Since`リクエストを送信し、サーバーにファイルが変更されたかどうかを尋ねることができます。これは弱い検証です。
- **サーバーの応答**：サーバーはファイルの検証を行うかどうかを選択することができます。検証しない場合は、直接200 OKステータスコードとリソースを返します。検証する場合は、304 Not Modifiedを返し、クライアントにキャッシュされたリソースを引き続き使用してもよいことを通知します。同時に、他のヘッダフィールドを返すこともでき、例えばキャッシュの有効期限を更新することができます。

## Vary応答
サーバーは応答にVaryヘッダを含めることができ、その値は応答ヘッダ内の特定のキー（例えばContent-Encoding）であり、リソースが特定のエンコーディングに対してキャッシュされることを示します。例えば：
1. **最初のリクエスト**：クライアントは`GET /resource HTTP/1.1`、`Accept-Encoding: *`を送信し、サーバーは`HTTP/1.1 200 OK`、`Content-Encoding: gzip`、`Vary: Content-Encoding`を返します。このとき、リソースとgzipエンコーディングのContent-Encodingが一緒にキャッシュされます。
2. **続くリクエスト**：クライアントは`GET /resource HTTP/1.1`、`Accept-Encoding: br`を送信します。現在のキャッシュされたリソースのエンコーディングはgzipであり、クライアントが期待するbrと異なるため、リソースをサーバーから再取得する必要があります。サーバーは`HTTP/1.1 200 OK`、`Content-Encoding: br`、`Vary: Content-Encoding`を返し、クライアントはbr形式のリソースを再度キャッシュします。次回、クライアントがbrタイプのリソースをリクエストするときに、キャッシュヒットすることができます。

Varyは、追加的にリソースを分類（例えばエンコーディングタイプ）することでキャッシュを実現しますが、その結果、リソースの重複保存が生じる可能性があります。この問題を解決するために、リソースのリクエストを標準化する必要があります。つまり、リクエストの前にエンコーディング方法を検証し、1つのエンコーディングを選択してリクエストを行い、複数のキャッシュを避ける必要があります。

## 結論
この記事では、HTTPキャッシュメカニズムを包括的に紹介しました。キャッシュの種類、応答ステータス、キャッシュ制御、キャッシュの更新、バージョン更新（Revving）、キャッシュの検証、Vary応答について説明しました。実際のアプリケーションでは、HTTPキャッシュメカニズムを深く理解し、合理的に適用することで、ウェブサイトのパフォーマンスとユーザーエクスペリエンスを向上させることができます。

# [Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/?lc_t=d_cache)

最後に、ウェブサービスをデプロイするのに最適なプラットフォームをおすすめします：**[Leapcell](https://leapcell.io/?lc_t=d_cache)**

[![brandpic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/63735a65-a510-4255-b52e-a598607ee344.png)](https://leapcell.io/?lc_t=d_cache)


### 🚀 好きな言語で構築しよう
JavaScript、Python、Go、またはRustで簡単に開発できます。  

### 🌍 無料で無制限のプロジェクトをデプロイ
使用した分だけ支払います — リクエストがなければ、請求もありません。  

### ⚡ 使った分だけ支払い、隠された費用はありません
アイドル期間の料金はなく、シームレスにスケーリングできます。  

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/f512679c-87ec-4ba0-ab84-0b3fdb639ba8.png)](https://leapcell.io/?lc_t=d_cache)


[📖 ドキュメントを探索する](https://docs.leapcell.io/)  

🔹 Twitterでフォローしてください：[@LeapcellHQ](https://x.com/LeapcellHQ)