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 се генерира от страна на сървъра и може да бъде както детерминистичен (еднакъв за еднакво съдържание, полезно за споделени кешове), така и уникален за всеки отговор (за строга валидация). В REST API, предназначени за мобилна синхронизация, най-често се използва комбинация от хеш на съдържанието и номер на версия на записа в базата данни.
Силни ETag (strong ETag) — идентификатори, които се променят при всяка промяна на съдържанието, включително незначителни (интервали, форматиране). Формат: "abc123def" (в кавички, без префикс). Силните ETag гарантират, че ресурсът не се е променил байт по байт. Те са задължителни за заявки за диапазон (Range requests) и за проверка на цялостността на частични изтегляния.
Слаби ETag (weak ETag) — идентификатори с префикс W/, например W/"abc123def". Те допускат ресурсът да бъде семантично еквивалентен, дори ако байтовото представяне е различно. Слабите ETag са полезни за сървъри, които динамично генерират отговори с различни интервали или форматиране, но с еднакво значение. Слабите ETag обаче не поддържат заявки за диапазон.
Сравнение на видовете ETag:
| Характеристика | Силен ETag | Слаб ETag |
|---|---|---|
| Формат | "hash" | W/"hash" |
| Чувствителност | Байт по байт | Семантична |
| Заявки за диапазон | Поддържат се | Не се поддържат |
| Кеширане 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 се използва в комбинация с разрешаване на конфликти. Клиентът синхронизира, като получава текущите ETag за всички ресурси. При изпращане на промени сървърът проверява If-Match — ако ETag не съвпада, се регистрира конфликт, който се разрешава според избраната стратегия (LWW, Merge). Според Postman API Report (2025), 67% от продукционните 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също