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" |
| Осетљивост | Бајт по бајт | Семантичка |
| 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 се користи у комбинацији са решавањем конфликата. Клијент се синхронизује добијајући актуелне 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође