ETag — HTTP-заголовок відповіді, що містить унікальний ідентифікатор версії ресурсу. Сервер генерує ETag як хеш вмісту або номер версії та повертає клієнту разом із даними. При наступних запитах клієнт надсилає цей ідентифікатор у заголовку If-None-Match, дозволяючи серверу перевірити, чи змінився ресурс. За даними MDN Web Docs, 2025, ETag є основою механізму умовних GET-запитів у HTTP. Умовні запити з ETag скорочують обсяг даних, що передаються під час синхронізації мобільних застосунків, до 90%.
Головне
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), використання ETag у мобільних API скорочує середній обсяг відповіді на 87% для списків та на 94% для окремих об'єктів.
ETag генерується на стороні сервера і може бути як детермінованим (однаковий для однакового вмісту, що корисно для shared кешів), так і унікальним для кожної відповіді (для суворої валідації). У REST API, призначених для мобільної синхронізації, найчастіше використовується комбінація хешу вмісту та номера версії запису в базі даних.
Сильні ETag (strong ETag) — ідентифікатори, які змінюються при будь-якій зміні вмісту, включаючи незначні (пробіли, форматування). Формат: «abc123def» (у подвійних лапках, без префікса). Сильні ETag гарантують, що ресурс не змінився байт-в-байт. Вони обов'язкові для діапазонних запитів (Range requests) та для перевірки цілісності часткових завантажень.
Слабкі ETag (weak ETag) — ідентифікатори з префіксом W/, наприклад W/«abc123def». Вони допускають, що ресурс семантично еквівалентний, навіть якщо байтове представлення відрізняється. Слабкі ETag корисні для серверів, які динамічно генерують відповіді з різними пробілами або форматом, але однаковим змістом. Однак слабкі ETag не підтримують діапазонні запити.
Порівняння типів ETag:
| Характеристика | Сильний ETag | Слабкий ETag |
|---|---|---|
| Формат | «hash» | W/«hash» |
| Чутливість | Байт-в-байт | Семантична |
| Range запити | Підтримуються | Не підтримуються |
| Кешування CDN | Ідеально | Обмежено |
| Синхронізація | Висока точність | Допускає колізії |
Last-Modified — HTTP-заголовок, що вказує дату та час останньої зміни ресурсу. Клієнт надсилає його назад у заголовку If-Modified-Since. Last-Modified простіший у реалізації (серверу потрібна лише дата), але має фундаментальні обмеження: роздільна здатність в одну секунду (дві зміни за одну секунду нерозрізненні) та неможливість визначити, чи змінився вміст при тому ж часі (наприклад, після відновлення з бекапу).
ETag вирішує ці проблеми: хеш вмісту змінюється при будь-якій зміні незалежно від часу. Тому сучасні REST API використовують комбінацію обох заголовків: ETag для точної валідації та Last-Modified для приблизної фільтрації на CDN. Apache HTTP Server та Nginx за замовчуванням генерують обидва заголовки для статичних файлів.
Для мобільних застосунків із синхронізацією ETag критичніший, оскільки він дозволяє виявляти конфлікти редагування. Якщо клієнт надсилає PUT-заголовок із If-Match: «etag», сервер відхиляє запит, якщо ресурс було змінено іншим клієнтом (оптимістичне блокування). Last-Modified не може гарантувати такої надійності через часову точність.
Розглянемо клієнтську реалізацію ETag у мобільному застосунку на Kotlin із використанням Retrofit та OkHttp. При кожному GET-запиті клієнт зберігає ETag із відповіді, а при наступному запиті надсилає його в заголовку If-None-Match. Якщо сервер повертає 304, дані не завантажуються повторно.
Налаштування OkHttp-клієнта з кешуванням ETag:
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}"))
}
}
}
Клієнт зберігає ETag після успішної відповіді 200 та надсилає його в заголовку If-None-Match при наступному запиті. При 304 клієнт знає, що локальна версія актуальна, і не витрачає трафік на повторне завантаження даних. Цей патерн скорочує мережеві витрати мобільного застосунку на 80–90% для часто запитуваних ресурсів.
ETag є ключовим механізмом для оптимізації синхронізації мобільних застосунків із REST API. При стандартній схемі синхронізації клієнт спочатку запитує список ресурсів із ETag-валідацією — якщо жоден ресурс не змінився, сервер повертає 304 і клієнт завершує синхронізацію. Якщо зміни є, сервер повертає лише змінені ресурси. Такий підхід називається дельта-синхронізацією і критично важливий для мобільних пристроїв з обмеженим трафіком.
У сценаріях з оптимістичним блокуванням ETag використовується для запобігання конфліктам Lost Update. Коли клієнт надсилає PUT-запит на оновлення ресурсу, він включає заголовок If-Match: «etag». Якщо ETag не збігається (інший клієнт вже змінив ресурс), сервер відповідає 412 Precondition Failed, і клієнт повинен заново завантажити актуальну версію та повторити зміну. Цей підхід забезпечує узгодженість даних без блокувань на рівні бази даних.
Для розподілених систем з офлайн-режимом ETag використовується в комбінації з Conflict Resolution. Клієнт синхронізується, отримуючи актуальні ETag для всіх ресурсів. При надсиланні змін сервер перевіряє If-Match — якщо ETag не збігся, реєструється конфлікт, який розв'язується за обраною стратегією (LWW, Merge). За даними Postman API Report (2025), 67% production REST API для мобільних застосунків використовують ETag як основний механізм валідації версій.
Часто задавані питання
ETag — HTTP-заголовок відповіді, що містить унікальний ідентифікатор версії ресурсу. Клієнт використовує його для умовних запитів: якщо ресурс не змінився, сервер повертає 304 Not Modified без тіла відповіді, економлячи трафік.
ETag використовує хеш вмісту для точного порівняння. Last-Modified базується на даті зміни з точністю до секунди. ETag надійніший для виявлення реальних змін і підтримує оптимістичне блокування через If-Match.
Сильні ETag (без префікса) розрізняють ресурси байт-в-байт. Слабкі ETag (з префіксом W/) допускають семантичну еквівалентність. Сильні потрібні для діапазонних запитів, слабкі — для динамічно генерованого контенту.
ETag скорочує трафік на 80–90%: клієнт перевіряє актуальність всіх ресурсів через If-None-Match, завантажуючи лише змінені. Без ETag клієнт завантажував би повні дані при кожній синхронізації, витрачаючи трафік та батарею.
Сервер обчислює ETag як хеш (MD5, SHA-256) вмісту відповіді або використовує номер версії запису з бази даних. У Spring Boot достатньо анотації @Cacheable з etag = true. В Express.js — middleware etag включено за замовчуванням.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.