アプリケーションにおけるETag — その概要、目的、および原理

著者: IT Sectr 公開日: 2026-06-14 読了時間: 7 分

ETagは、リソースバージョンの一意の識別子を含むHTTPレスポンスヘッダーです。サーバーはETagをコンテンツハッシュまたはバージョン番号として生成し、データとともにクライアントに返します。後続のリクエストでは、クライアントはこの識別子をIf-None-Matchヘッダーで送信し、サーバーがリソースの変更を確認できるようにします。MDN Web Docs(2025年)によると、ETagはHTTPにおける条件付きGETリクエストメカニズムの基盤です。ETagを使用した条件付きリクエストにより、モバイルアプリケーションの同期中に転送されるデータ量を最大90%削減できます。

主要ポイント

  • ETag — リソースバージョンの一意の識別子を含むHTTPヘッダー。通常はコンテンツのハッシュ。
  • If-None-Match — クライアントが保存済みのETagを送信し、リソースが変更されていない場合サーバーは304 Not Modifiedを返す。
  • トラフィック節約 — ETagを使用した条件付きリクエストは、レスポンス本文が送信されないため、モバイルアプリ同期中のデータ量を削減する。
  • 強ETagと弱ETag — 強ETagはコンテンツをバイト単位で区別し、弱ETagはリソースの意味的等価性を許容する。
  • 使用例 — ETagはREST APIでのデータ同期、キャッシュ、編集競合の防止に使用される。

HTTPとモバイルアプリケーションにおけるETagとは?

ETag(Entity Tag)は、条件付きヘッダーファミリーに属するHTTPヘッダーで、キャッシュされたリソースを検証します。サーバーはETagをハッシュ(MD5、SHA-256)またはリソースバージョン番号として計算し、GETリクエストへのレスポンスとして返します。クライアントはETagをデータとともに保存し、後続のリクエストでIf-None-Matchヘッダーで送信します。リソースコンテンツが変更されていない場合、サーバーはレスポンス本文なしで304 Not Modifiedステータスで応答します。

モバイルアプリケーションにとってETagは非常に重要です。なぜなら、ダウンロードされるデータ量を削減するからです。起動または同期のたびに、アプリはIf-None-Matchリクエストでリソースの新鮮さをチェックします — 完全なデータをロードする代わりに、304を受け取ってローカルコピーを使用します。Google Chrome Team(2024年)によると、モバイルAPIでETagを使用すると、リストの平均レスポンスサイズが87%、個別オブジェクトで94%削減されます。

ETagはサーバー側で生成され、決定論的(同一コンテンツに対して同一、共有キャッシュに有用)またはレスポンスごとに一意(厳格な検証用)のいずれかになります。モバイル同期向けに設計されたREST APIでは、コンテンツハッシュとデータベースのレコードバージョン番号の組み合わせが最も一般的です。

ETagの種類:強識別子と弱識別子

強ETag(strong ETag)は、わずかな変更(空白、フォーマット)を含むコンテンツの変更に応じて変化する識別子です。形式:“abc123def”(二重引用符で囲み、プレフィックスなし)。強ETagはリソースがバイト単位で変更されていないことを保証します。これらは範囲リクエスト(Range requests)および部分ダウンロードの整合性検証に必須です。

弱ETag(weak ETag)は、W/プレフィックスが付いた識別子です。例:W/“abc123def”。これらは、バイト表現が異なっていてもリソースが意味的に同等であることを許容します。弱ETagは、異なる空白やフォーマットで動的にレスポンスを生成するが意味は同じサーバーに役立ちます。ただし、弱ETagは範囲リクエストをサポートしません。

ETagの種類の比較:

特性強ETag弱ETag
形式“hash”W/“hash”
感度バイト単位意味的
範囲リクエストサポート非サポート
CDNキャッシュ理想的制限的
同期高精度衝突を許容

ETag vs Last-Modified:どちらを選ぶか

Last-Modifiedは、リソースの最終更新日時を示すHTTPヘッダーです。クライアントはこれをIf-Modified-Sinceヘッダーで送り返します。Last-Modifiedは実装が簡単ですが(サーバーは日付のみ必要)、基本的な制限があります:1秒の分解能(同じ秒内の2つの変更は区別不可)、およびタイムスタンプが同じ場合にコンテンツが変更されたかどうかを判断できない(バックアップ復元後など)。

ETagはこれらの問題を解決します:コンテンツハッシュは時間に関係なく変更に応じて変化します。そのため、現代のREST APIは両方のヘッダーを組み合わせて使用します:ETagは正確な検証用、Last-ModifiedはCDNでのおおまかなフィルタリング用です。Apache HTTP ServerとNginxはデフォルトで静的ファイルに対して両方のヘッダーを生成します。

同期機能を持つモバイルアプリケーションでは、ETagの方が重要です。編集競合を検出できるからです。クライアントがPUTリクエストをIf-Match: “etag”付きで送信すると、別のクライアントによってリソースが変更された場合、サーバーはリクエストを拒否します(楽観的ロック)。Last-Modifiedは秒単位の精度のため、そのような信頼性を保証できません。

KotlinでのETag使用例

クライアント側の実装を見てみましょう。RetrofitとOkHttpを使用したKotlinでのモバイルアプリにおけるETagの実装です。GETリクエストごとに、クライアントはレスポンスからETagを保存し、次のリクエストでIf-None-Matchヘッダーで送信します。サーバーが304を返すと、データは再ダウンロードされません。

ETagキャッシュを使用したOkHttpクライアントの設定:

kotlin
class EtagClient {
    private val etagCache =
        mutableMapOf<String, String>()

    private val client = OkHttpClient.Builder().build()

    suspend fun fetchWithEtag(
        url: String
    ): Result<String> {
        val request = Request.Builder()
            .url(url)
            .header("If-None-Match",
                etagCache[url] ?: "")
            .build()

        val response = client.newCall(request).await()

        return when (response.code) {
            304 -> Result.success(
                "not_modified")
            200 -> {
                response.header("ETag")?.let {
                    etagCache[url] = it
                }
                Result.success(response.body?.string()
                    ?: "")
            }
            else -> Result.failure(
                Exception("HTTP ${response.code}"))
        }
    }
}

クライアントは成功した200レスポンスの後にETagを保存し、次のリクエストでIf-None-Matchヘッダーで送信します。304レスポンスの場合、クライアントはローカルバージョンが最新であることを認識し、再ダウンロードにトラフィックを浪費しません。このパターンにより、頻繁にリクエストされるリソースのモバイルアプリのネットワークコストが80~90%削減されます。

モバイルアプリ同期におけるETagの役割

ETagは、REST APIとのモバイルアプリ同期を最適化するための重要なメカニズムです。標準的な同期スキームでは、クライアントは最初にETag検証付きでリソースのリストをリクエストします — どのリソースも変更されていない場合、サーバーは304を返し、クライアントは同期を完了します。変更がある場合、サーバーは変更されたリソースのみを返します。このアプローチはデルタ同期と呼ばれ、トラフィックが制限されたモバイルデバイスにとって非常に重要です。

楽観的ロックのシナリオでは、ETagはLost Update競合を防ぐために使用されます。クライアントがPUTリクエストでリソースを更新する際、If-Match: “etag”ヘッダーを含めます。ETagが一致しない場合(別のクライアントがすでにリソースを変更した場合)、サーバーは412 Precondition Failedで応答し、クライアントは現在のバージョンを再取得して変更を再試行する必要があります。このアプローチは、データベースレベルのロックなしでデータの整合性を保証します。

オフラインモードの分散システムでは、ETagは競合解決と組み合わせて使用されます。クライアントはすべてのリソースの現在のETagを取得して同期します。変更を送信する際、サーバーはIf-Matchをチェックします — ETagが一致しない場合、競合が登録され、選択された戦略(LWW、マージ)に従って解決されます。Postman API Report(2025年)によると、モバイルアプリケーション向けのプロダクションREST APIの67%がバージョン検証の主要メカニズムとしてETagを使用しています。

よくある質問

ETag HTTPヘッダーとは何ですか?

ETagは、リソースバージョンの一意の識別子を含むHTTPレスポンスヘッダーです。クライアントは条件付きリクエストに使用します:リソースが変更されていない場合、サーバーはレスポンス本文なしで304 Not Modifiedを返し、トラフィックを節約します。

ETagとLast-Modifiedの違いは何ですか?

ETagは正確な比較にコンテンツハッシュを使用します。Last-Modifiedは秒単位の精度で変更日付に基づきます。ETagは実際の変更検出においてより信頼性が高く、If-Matchを介した楽観的ロックをサポートします。

強ETagと弱ETagとは何ですか?

強ETag(プレフィックスなし)はリソースをバイト単位で区別します。弱ETag(W/プレフィックス付き)は意味的等価性を許容します。強ETagは範囲リクエストに必要で、弱ETagは動的に生成されるコンテンツ用です。

ETagはモバイル同期にどのように役立ちますか?

ETagはトラフィックを80~90%削減します:クライアントはIf-None-Matchを介してすべてのリソースの新鮮さをチェックし、変更されたものだけをダウンロードします。ETagがない場合、クライアントは同期のたびに完全なデータをダウンロードし、トラフィックとバッテリーを浪費することになります。

サーバーでETagを実装するには?

サーバーはETagをレスポンスコンテンツのハッシュ(MD5、SHA-256)として計算するか、データベースのレコードバージョン番号を使用します。Spring Bootでは、@Cacheableアノテーションにetag = trueを指定するだけで十分です。Express.jsでは、etagミドルウェアがデフォルトで有効です。

まとめ

  • ETag — コンテンツハッシュまたはバージョン番号に基づくリソースバージョン検証用のHTTPヘッダー。
  • 条件付きリクエスト — クライアントは保存済みETagとともにIf-None-Matchを送信し、サーバーは変更がない場合は304で応答。
  • ETagの種類 — 強ETag(バイト単位、範囲リクエスト用)と弱ETag(意味的等価性、W/プレフィックス)。
  • 利点 — ETagはLast-Modifiedより正確。ハッシュは時間に関係なくコンテンツ変更に応じて変化するため。
  • 楽観的ロック — If-Matchを介して、ETagは同時リソース編集時のLost Update競合を防止。
  • デルタ同期 — ETagは変更されたリソースのみが送信される同期スキームを実現。
  • 推奨事項 — モバイルアプリケーションのREST APIには常にETagを追加。CDNおよびプロキシサーバー互換性のためにLast-Modifiedと組み合わせる。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください