Conditional GET — クライアントが完全なダウンロード前にキャッシュされたリソースの最新性を確認できるHTTPメカニズム。クライアントはIf-None-Match(ETagを含む)またはIf-Modified-Since(日付を含む)ヘッダーとともにGETリクエストを送信し、リソースが変更されていない場合、サーバーはレスポンスボディなしで304 Not Modifiedを返します。MDN Web Docs、2025によると、条件付きリクエストはサーバーとクライアントのネットワークトラフィックを削減します。304 Not Modifiedは、効率的なモバイルアプリケーション同期のための重要なHTTPステータスです。
重要なポイント
Conditional GETは、1つ以上の条件付きヘッダーを含むGETリクエストであり、サーバーはそれらに基づいて完全な応答を返すか、304 Not Modifiedステータスのみを返すかを決定します。主な目的は、前回のリクエスト以降にリソースが変更されていない場合に、応答ボディの送信を回避することです。これはRFC 7232で定義された基本的なHTTPキャッシングメカニズムです。
モバイルアプリケーションにとって、Conditional GETはネットワークトラフィックを最適化する最も効果的な方法の1つです。典型的なシナリオ:アプリを開くとき、クライアントはフィード、プロフィール、設定を読み込むために一連の条件付きGETリクエストを送信します。データが変更されていない場合、アプリは304を受信し、ローカルコピーを使用します。これには秒ではなくミリ秒かかり、モバイルデータを消費しません。
Google Web Fundamentals(2025)によると、モバイルアプリケーションに条件付きGETリクエストを実装すると、繰り返し訪問時の平均読み込み時間が40–60%短縮され、更新頻度の低いページのトラフィック使用量が70–90%削減されます。効果は特に低速接続(3G、Edge)で顕著であり、1バイト1バイトが重要です。
プロセスは3つのステップで構成されます。最初 — クライアントは通常のGETリクエストを送信し、サーバーはキャッシュヘッダー(ETag、Last-Modified)とともにリソースを返します。2番目 — クライアントはリソースとそのバリデーターをローカルに保存します。3番目 — 繰り返しリクエスト時に、クライアントはIf-None-Match(ETag用)および/またはIf-Modified-Since(Last-Modified用)とともにGETを送信します。サーバーはバリデーターをチェックし、リソースが変更されていない場合は304、新しいデータがある場合は200で応答します。
サーバーは両方のヘッダーが存在する場合、ETagをLast-Modifiedより優先します。これは、ETagの方がより正確な検証を提供するためです — コンテンツハッシュは変更があると変わりますが、Last-Modifiedは1秒の分解能しかありません。ETagが一致する場合、サーバーはLast-Modifiedをチェックせずに直ちに304を返します。
リクエストシーケンスにおける完全なConditional GETサイクルの例:
// ステップ 1: 最初のリクエスト — データとETagを取得
GET /api/profile
Response: 200 OK
ETag: "33a64df551425fcc55e"
Body: { "name": "Alice" }
// ステップ 2: リクエストを繰り返す — If-None-Match付き
GET /api/profile
If-None-Match: "33a64df551425fcc55e"
Response: 304 Not Modified
// レスポンスボディなし — ローカルコピーを使用
2番目のリクエストでは、サーバーはIf-None-MatchからのETagを現在のリソースハッシュと比較します。一致する場合、ボディなしで304を返します — クライアントはキャッシュされたデータを引き続き使用します。これがConditional GETの本質です:最小限のトラフィックで最大のデータ最新性。
通常のGETリクエストは常にボディ付きの完全な200 OK応答を返します。リソースが変更されていない場合でも、サーバーはすべてのデータを再度送信します。これは小さなリソースや稀なリクエストには許容されますが、起動のたびに数百のリクエストを行うモバイルアプリケーションでは、このアプローチは過剰なトラフィックとバッテリー消費につながります。
Conditional GETはヘッダーの形でオーバーヘッドを追加します(通常1リクエストあたり50–200バイト)が、304応答でキロバイトやメガバイトを節約します。リソースが大きいほど、条件付きリクエストのメリットが大きくなります。10 KB以上の画像、データリスト、JSONドキュメントの場合、Conditional GETは最初の繰り返しリクエストから効果を発揮します。
2つのアプローチの比較特性:
| パラメータ | 通常のGET | Conditional GET |
|---|---|---|
| トラフィック(変更なし) | 完全な応答 | ヘッダーのみ(~200バイト) |
| レイテンシ | 完全ダウンロード | ミリ秒(304) |
| サーバー負荷 | 生成+転送 | ETagチェックのみ |
| 実装の複雑さ | 最小 | ETag保存が必要 |
| 大規模データの効率 | 低い | 高い |
完全な実装を見てみましょう — OkHttpとRoomを使用したKotlinでのConditional GET(ETag保存用)。タスクリストアプリケーションがサーバーからタスクを読み込み、トラフィックを最小限に抑えるために条件付きリクエストを使用します。ETagはセッション間で永続化するためにローカルデータベースに保存されます。
KotlinでのConditional GETを持つリポジトリ:
class TaskRepository(
private val api: TaskApi,
private val etagDao: EtagDao
) {
suspend fun getTasks(): List<Task> {
val savedEtag = etagDao.getEtag("tasks")
val response = api.fetchTasks(
ifNoneMatch = savedEtag
)
return when (response.code()) {
304 -> taskDao.getAll() // ローカルキャッシュから
200 -> {
response.header("ETag")?.let {
etagDao.saveEtag("tasks", it)
}
val tasks = response.body() ?: emptyList()
taskDao.replaceAll(tasks)
tasks
}
else -> throw Exception(
"Sync failed: ${response.code()}")
}
}
}
TaskRepositoryは応答コードをチェックします:304は変更なしを意味し、データはローカルのRoomキャッシュから返されます。200の場合、新しいETagが保存され、タスクがローカルデータベースで更新されます。このパターンはREST API同期を備えたモバイルアプリケーションの標準です。
Conditional GETは広く使用されています — モバイルアプリケーションでのデータ同期最適化に。主なシナリオ:ニュースフィードの読み込み(Twitter、Instagramは定期的にIf-None-Match付きでAPIをポーリング)、ユーザープロフィールの更新、通知リストの読み込み、タスクの同期。各ケースで、アプリはデータを再ダウンロードせずに最新性を確認できます。
オフラインファーストアプリケーションの場合、Conditional GETは同期の最初の段階として機能します。アプリはまず、前回の同期以降にローカルで変更されたすべてのリソースに対して条件付きGETリクエストを送信します。304のリソースはダウンロード不要です。その後、アプリはローカルの変更に対してPUT/POSTを送信します。この2フェーズアプローチにより、トラフィック消費を最小限に抑えます。
競合解決と組み合わせることで、Conditional GETは効率的な競合検出を可能にします。クライアントが新しいデータとともに200を受信したが(リソースが変更された)、未送信のローカル変更がある場合 — 競合が登録されます。クライアントはLWWを適用するか(ローカル変更が失われる)、マージ戦略を開始してローカルとリモートの変更をマージできます。Meta Engineering Blog(2025)によると、MessengerでのConditional GET実装により、平均同期トラフィックが73%削減されました。
よくある質問
Conditional GET — 条件付きヘッダー(If-None-Match、If-Modified-Since)付きのHTTP GETリクエスト。リソースが変更されていない場合はサーバーが304 Not Modifiedを返し、新しいデータがある場合は200を返します。効率的なキャッシングメカニズムです。
通常のGETは常にボディ付きの完全な応答を返します。Conditional GETはバージョンチェックヘッダー(ETag、日付)を追加します。データが変更されていない場合、サーバーはボディなしで304を返し、トラフィックと読み込み時間を節約します。
効果的なキャッシングのために、各サーバー応答からETagとLast-Modifiedをローカルデータベースに保存します。次のリクエスト時に、それらをIf-None-MatchおよびIf-Modified-Sinceヘッダーで送信します。304の場合、ローカルキャッシュのデータを使用します。
304応答時、サーバーはレスポンスボディを送信せず — ヘッダーのみ(~200バイト)を送信します。50 KBのリソースの場合、99.6%のトラフィック節約になります。1日50回同期するアプリの場合、月間で数十メガバイトの節約になります。
はい、これは標準的なアプローチです — デルタ同期において。クライアントはConditional GETを介して各リソースの最新性を確認し、変更されたリソースのみをダウンロードし、ローカルの変更を送信します。このアプローチはTwitter、Instagram、Telegram、およびほとんどの最新APIで使用されています。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。