“Conditional GET” — an HTTP mechanism that allows a client to check the relevance of a cached resource before a full download. The client sends a GET request with “If-None-Match” (containing ETag) or “If-Modified-Since” (containing a date) headers, and the server returns 304 Not Modified without a response body if the resource has not changed. According to MDN Web Docs, 2025, conditional requests reduce server and client network traffic. 304 Not Modified is a key HTTP status for efficient mobile application synchronization.
Key Takeaways
Conditional GET is a GET request that includes one or more conditional headers, based on which the server decides whether to return a full response or just the 304 Not Modified status. The main goal is to avoid transmitting the response body if the resource has not changed since the last request. This is a fundamental HTTP caching mechanism defined in RFC 7232.
For mobile applications, Conditional GET is one of the most effective ways to optimize network traffic. A typical scenario: when opening an app, the client sends a series of conditional GET requests to load the feed, profile, and settings. If the data has not changed, the app receives 304 and uses the local copy. This takes milliseconds instead of seconds and does not consume mobile data.
According to Google Web Fundamentals (2025), implementing Conditional GET requests in a mobile application reduces average load time by 40–60% for repeat visits and cuts traffic usage by 70–90% for pages with infrequent updates. The effect is especially noticeable on slow connections (3G, Edge), where every byte counts.
The process consists of three steps. First — the client sends a regular GET request, the server returns the resource along with caching headers (ETag, Last-Modified). Second — the client saves the resource and its validators locally. Third — on a repeat request, the client sends a GET with “If-None-Match” (for ETag) and/or “If-Modified-Since” (for Last-Modified). The server checks the validators and responds 304 if the resource has not changed, or 200 with new data.
The server uses ETag priority over Last-Modified when both headers are present. This is because ETag provides more accurate validation — the content hash changes with any modification, whereas Last-Modified has a one-second resolution. If the ETag matches, the server immediately returns 304 without checking Last-Modified.
Example of a complete Conditional GET cycle in a request sequence:
// Step 1: First request — get data and ETag
GET /api/profile
Response: 200 OK
ETag: "33a64df551425fcc55e"
Body: { "name": "Alice" }
// Step 2: Repeat request — with If-None-Match
GET /api/profile
If-None-Match: "33a64df551425fcc55e"
Response: 304 Not Modified
// Response body is absent — use local copy
In the second request, the server compares the ETag from “If-None-Match” with the current resource hash. If they match, it returns 304 without a body — the client continues using cached data. This is the essence of Conditional GET: minimal traffic with maximum data relevance.
A regular GET request always returns a full 200 OK response with a body. Even if the resource has not changed, the server transmits all data again. This is acceptable for small resources or infrequent requests, but for mobile applications with hundreds of requests on each launch, this approach leads to excessive traffic and battery drain.
Conditional GET adds overhead in the form of headers (usually 50–200 bytes per request) but saves kilobytes and megabytes with a 304 response. The larger the resource, the more beneficial the conditional request. For images, data lists, and JSON documents from 10 KB and up, Conditional GET pays off from the very first repeat request.
Comparative characteristics of the two approaches:
| Parameter | Regular GET | Conditional GET |
|---|---|---|
| Traffic (no changes) | Full response | Headers only (~200 bytes) |
| Latency | Full download | Milliseconds (304) |
| Server load | Generation + transfer | ETag check only |
| Implementation complexity | Minimal | Requires ETag storage |
| Efficiency for large data | Low | High |
Let’s look at a full implementation of Conditional GET in Kotlin using OkHttp and Room for ETag storage. A task list application loads tasks from the server and uses conditional requests to minimize traffic. ETags are stored in a local database to persist between sessions.
Repository with Conditional GET in Kotlin:
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() // from local cache
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 checks the response code: 304 means no changes, and data is returned from the local Room cache. On 200, a new ETag is saved and tasks are updated in the local database. This pattern is a standard for mobile applications with REST API synchronization.
Conditional GET is widely used in mobile applications for data synchronization optimization. Main scenarios: loading news feeds (Twitter, Instagram periodically poll the API with “If-None-Match”), updating user profiles, loading notification lists, and syncing tasks. In each case, the app can check data relevance without re-downloading it.
For offline-first applications, Conditional GET serves as the first stage of synchronization. The app first sends conditional GET requests for all resources that have been locally modified since the last sync. Resources with 304 do not require downloading. After that, the app sends PUT/POST for local changes. This two-phase approach ensures minimal traffic consumption.
In combination with Conflict Resolution, Conditional GET allows efficient conflict detection. If the client receives 200 with new data (resource has changed) but has unsent local changes — a conflict is registered. The client can either apply LWW (local changes are lost) or launch a Merge Strategy to merge local and remote changes. According to Meta Engineering Blog (2025), implementing Conditional GET in Messenger reduced average sync traffic by 73%.
Frequently Asked Questions
Conditional GET — an HTTP GET request with conditional headers (“If-None-Match”, “If-Modified-Since”). The server returns 304 Not Modified if the resource has not changed, or 200 with new data. This is an efficient caching mechanism.
A regular GET always returns a full response with a body. Conditional GET adds version check headers (ETag, date). If the data has not changed, the server responds 304 without a body, saving traffic and load time.
For effective caching, save ETag and Last-Modified from each server response in a local database. On the next request, send them in the “If-None-Match” and “If-Modified-Since” headers. On 304, use data from the local cache.
On a 304 response, the server does not transmit the response body — only headers (~200 bytes). For a 50 KB resource, this means a 99.6% traffic saving. For an app that syncs 50 times a day, savings reach tens of megabytes per month.
Yes, this is the standard approach for delta synchronization. The client checks each resource’s relevance via Conditional GET, downloads only changed ones, and sends local changes. This approach is used in Twitter, Instagram, Telegram, and most modern APIs.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also