ETag в приложенията — какво е, предназначение и принцип

Автор: IT Sectr Публикувано: 2026-06-14 Време за четене: 7 мин

ETag — HTTP хедър на отговор, съдържащ уникален идентификатор на версията на ресурса. Сървърът генерира ETag като хеш на съдържанието или номер на версия и го връща на клиента заедно с данните. При последващи заявки клиентът изпраща този идентификатор в хедъра If-None-Match, позволявайки на сървъра да провери дали ресурсът се е променил. Според MDN Web Docs, 2025, ETag е основата на механизма за условни GET заявки в HTTP. Условните заявки с ETag намаляват обема на предаваните данни при синхронизация на мобилни приложения до 90%.

Основни точки

  • ETag — HTTP хедър, съдържащ уникален идентификатор на версията на ресурса, обикновено хеш на неговото съдържание.
  • If-None-Match — клиентът изпраща записания ETag, сървърът връща 304 Not Modified, ако ресурсът не се е променил.
  • Икономия на трафик — условните заявки с ETag намаляват обема на данните при синхронизация на мобилни приложения, тъй като тялото на отговора не се предава.
  • Силни и слаби ETag — силните различават съдържанието байт по байт, слабите допускат семантична еквивалентност на ресурса.
  • Приложение — ETag се използва в REST API за синхронизация на данни, кеширане и предотвратяване на конфликти при редактиране.

Какво е ETag в HTTP и мобилните приложения?

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: силни и слаби идентификатори

Силни ETag (strong ETag) — идентификатори, които се променят при всяка промяна на съдържанието, включително незначителни (интервали, форматиране). Формат: "abc123def" (в кавички, без префикс). Силните ETag гарантират, че ресурсът не се е променил байт по байт. Те са задължителни за заявки за диапазон (Range requests) и за проверка на цялостността на частични изтегляния.

Слаби ETag (weak ETag) — идентификатори с префикс W/, например W/"abc123def". Те допускат ресурсът да бъде семантично еквивалентен, дори ако байтовото представяне е различно. Слабите ETag са полезни за сървъри, които динамично генерират отговори с различни интервали или форматиране, но с еднакво значение. Слабите ETag обаче не поддържат заявки за диапазон.

Сравнение на видовете ETag:

ХарактеристикаСилен ETagСлаб ETag
Формат"hash"W/"hash"
ЧувствителностБайт по байтСемантична
Заявки за диапазонПоддържат сеНе се поддържат
Кеширане CDNИдеалноОграничено
СинхронизацияВисока точностДопуска колизии

ETag срещу Last-Modified: какво да изберем

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

Нека разгледаме клиентската имплементация на ETag в мобилно приложение на Kotlin, използвайки Retrofit и OkHttp. При всяка GET заявка клиентът запазва ETag от отговора, а при следващата заявка го изпраща в хедъра If-None-Match. Ако сървърът върне 304, данните не се изтеглят отново.

Конфигуриране на OkHttp клиент с ETag кеширане:

kotlin
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 в синхронизацията на мобилни приложения

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 като основен механизъм за валидация на версии.

Често задавани въпроси

Какво е HTTP хедърът ETag?

ETag — HTTP хедър на отговор, съдържащ уникален идентификатор на версията на ресурса. Клиентът го използва за условни заявки: ако ресурсът не се е променил, сървърът връща 304 Not Modified без тяло на отговора, спестявайки трафик.

Каква е разликата между ETag и Last-Modified?

ETag използва хеш на съдържанието за точно сравнение. Last-Modified се основава на датата на промяна с точност до секунда. ETag е по-надежден за откриване на реални промени и поддържа оптимистично заключване чрез If-Match.

Какво представляват силните и слабите ETag?

Силните ETag (без префикс) различават ресурсите байт по байт. Слабите ETag (с префикс W/) допускат семантична еквивалентност. Силните са необходими за заявки за диапазон, слабите — за динамично генерирано съдържание.

Как ETag помага в мобилната синхронизация?

ETag намалява трафика с 80–90%: клиентът проверява актуалността на всички ресурси чрез If-None-Match, изтегляйки само променените. Без ETag клиентът би изтеглял пълни данни при всяка синхронизация, харчейки трафик и батерия.

Как да имплементираме ETag на сървъра?

Сървърът изчислява ETag като хеш (MD5, SHA-256) на съдържанието на отговора или използва номера на версията на записа от базата данни. В Spring Boot, анотацията @Cacheable с etag = true е достатъчна. В Express.js, middleware etag е включен по подразбиране.

Резюме

  • ETag — HTTP хедър за валидация на версии на ресурси, базиран на хеш на съдържанието или номер на версия.
  • Условни заявки — клиентът изпраща If-None-Match с записан ETag, сървърът отговаря 304 при липса на промени.
  • Видове ETag — силни (байт по байт, за заявки за диапазон) и слаби (семантична еквивалентност, префикс W/).
  • Предимство — ETag е по-точен от Last-Modified, тъй като хешът се променя при всяка промяна на съдържанието независимо от времето.
  • Оптимистично заключване — чрез If-Match, ETag предотвратява Lost Update конфликти при конкурентно редактиране на ресурси.
  • Делта синхронизация — схеми за синхронизация на базата на ETag, при които се предават само променените ресурси.
  • Препоръка — винаги добавяйте ETag в REST API за мобилни приложения. Комбинирайте с Last-Modified за съвместимост с CDN и прокси сървъри.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също