Conditional GET (условный GET-запрос) — HTTP-механизм, позволяющий клиенту проверить актуальность кэшированного ресурса перед полной загрузкой. Клиент отправляет GET-запрос с заголовками If-None-Match (содержит ETag) или If-Modified-Since (содержит дату), и сервер возвращает 304 Not Modified без тела ответа, если ресурс не изменился. По данным MDN Web Docs, 2025, условные запросы сокращают сетевой трафик серверов и клиентов. 304 Not Modified — ключевой HTTP-статус для эффективной синхронизации мобильных приложений.
Главное
Conditional GET — это GET-запрос, содержащий один или несколько условных заголовков, на основании которых сервер решает, возвращать полный ответ или только статус 304 Not Modified. Основная цель — избежать передачи тела ответа, если ресурс не изменился с момента последнего запроса. Это фундаментальный механизм HTTP-кэширования, определённый в спецификации RFC 7232.
Для мобильных приложений Conditional GET — один из самых эффективных способов оптимизации сетевого трафика. Типичный сценарий: при открытии приложения клиент отправляет серию условных GET-запросов для загрузки ленты, профиля и настроек. Если данные не изменились, приложение получает 304 и использует локальную копию. Это занимает миллисекунды вместо секунд и не расходует мобильный трафик.
По данным Google Web Fundamentals (2025), внедрение Conditional GET-запросов в мобильном приложении сокращает среднее время загрузки на 40–60% для повторных посещений и снижает расход трафика на 70–90% для страниц с редкими обновлениями. Особенно заметен эффект на медленных соединениях (3G, Edge), где каждый байт на счету.
Процесс состоит из трёх шагов. Первый — клиент отправляет обычный GET-запрос, сервер возвращает ресурс вместе с заголовками кэширования (ETag, Last-Modified). Второй — клиент сохраняет ресурс и его валидаторы локально. Третий — при повторном запросе клиент отправляет GET с If-None-Match (для ETag) и/или If-Modified-Since (для Last-Modified). Сервер проверяет валидаторы и отвечает 304, если ресурс не изменился, либо 200 с новыми данными.
Сервер использует приоритет ETag перед Last-Modified при наличии обоих заголовков. Это связано с тем, что ETag обеспечивает более точную валидацию — хеш содержимого меняется при любом изменении, тогда как Last-Modified имеет разрешение в одну секунду. Если ETag совпадает, сервер сразу возвращает 304, не проверяя Last-Modified.
Пример полного цикла Conditional GET в последовательности запросов:
// 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
Во втором запросе сервер сравнивает ETag из If-None-Match с текущим хешем ресурса. При совпадении возвращается 304 без тела — клиент продолжает использовать кэшированные данные. Это и есть суть Conditional GET: минимум трафика при максимальной актуальности данных.
Обычный GET-запрос всегда возвращает полный ответ 200 OK с телом. Даже если ресурс не изменился, сервер передаёт все данные заново. Это приемлемо для небольших ресурсов или при редких запросах, но для мобильных приложений с сотнями запросов при каждом запуске такой подход приводит к избыточному расходу трафика и батареи.
Conditional GET добавляет накладные расходы в виде заголовков (обычно 50–200 байт на запрос), но экономит килобайты и мегабайты при ответе 304. Чем больше ресурс, тем выгоднее условный запрос. Для изображений, списков данных и JSON-документов размером от 10 КБ Conditional GET окупается с первого же повторного запроса.
Сравнительная характеристика двух подходов:
| Параметр | Обычный GET | Conditional GET |
|---|---|---|
| Трафик (нет изменений) | Полный ответ | Только заголовки (~200 байт) |
| Задержка | Полная загрузка | Миллисекунды (304) |
| Нагрузка на сервер | Генерация + передача | Только проверка ETag |
| Сложность реализации | Минимальная | Требует хранения ETag |
| Эффективность для больших данных | Низкая | Высокая |
Рассмотрим полноценную реализацию Conditional GET на Kotlin с использованием OkHttp и Room для хранения ETag. Приложение списка задач загружает задачи с сервера и использует условные запросы для минимизации трафика. ETag хранятся в локальной базе данных для сохранения между сессиями.
Репозиторий с Conditional GET на 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 проверяет код ответа: 304 означает отсутствие изменений, и данные возвращаются из локального кэша Room. При 200 новый ETag сохраняется, а задачи обновляются в локальной базе. Этот паттерн — стандарт для мобильных приложений с синхронизацией через REST API.
Conditional GET широко используется в мобильных приложениях для оптимизации синхронизации данных. Основные сценарии: загрузка ленты новостей (Twitter, Instagram периодически опрашивают API с If-None-Match), обновление профиля пользователя, загрузка списка уведомлений и синхронизация задач. В каждом случае приложение может проверять актуальность данных, не загружая их повторно.
Для офлайн-first приложений Conditional GET служит первым этапом синхронизации. Приложение сначала отправляет условные GET-запросы для всех ресурсов, которые были изменены локально с последней синхронизации. Ресурсы с 304 не требуют загрузки. После этого приложение отправляет PUT/POST для локальных изменений. Такой двухфазный подход обеспечивает минимальный расход трафика.
В комбинации с Conflict Resolution Conditional GET позволяет эффективно детектировать конфликты. Если клиент получил 200 с новыми данными (ресурс изменился), но у клиента есть неотправленные локальные изменения — регистрируется конфликт. Клиент может либо применить LWW (локальные изменения теряются), либо запустить Merge Strategy для объединения локальных и удалённых изменений. По данным Meta Engineering Blog (2025), внедрение Conditional GET в Messenger сократило средний расход трафика на синхронизацию на 73%.
Часто задаваемые вопросы
Conditional GET — HTTP GET-запрос с условными заголовками (If-None-Match, If-Modified-Since). Сервер возвращает 304 Not Modified, если ресурс не изменился, или 200 с новыми данными. Это механизм эффективного кэширования.
Обычный GET всегда возвращает полный ответ с телом. Conditional GET добавляет заголовки проверки версии (ETag, дата). Если данные не изменились, сервер отвечает 304 без тела, экономя трафик и время загрузки.
Для эффективного кэширования сохраняйте ETag и Last-Modified из каждого ответа сервера в локальную базу данных. При следующем запросе отправляйте их в заголовках If-None-Match и If-Modified-Since. При 304 используйте данные из локального кэша.
При 304 ответе сервер не передаёт тело ответа — только заголовки (~200 байт). Для ресурса размером 50 КБ это означает экономию 99.6% трафика. Для приложения, которое синхронизируется 50 раз в день, экономия достигает десятков мегабайт в месяц.
Да, это стандартный подход для дельта-синхронизации. Клиент проверяет актуальность каждого ресурса через Conditional GET, загружает только изменившиеся и отправляет локальные изменения. Такой подход используется в Twitter, Instagram, Telegram и большинстве современных API.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.