ETag — nagłówek odpowiedzi HTTP zawierający unikalny identyfikator wersji zasobu. Serwer generuje ETag jako hash treści lub numer wersji i zwraca go klientowi wraz z danymi. Przy kolejnych żądaniach klient wysyła ten identyfikator w nagłówku If-None-Match, umożliwiając serwerowi sprawdzenie, czy zasób się zmienił. Według MDN Web Docs, 2025, ETag stanowi podstawę mechanizmu warunkowych żądań GET w HTTP. Żądania warunkowe z ETag zmniejszają ilość przesyłanych danych podczas synchronizacji aplikacji mobilnych nawet o 90%.
Najważniejsze
ETag (Entity Tag) — nagłówek HTTP z rodziny nagłówków warunkowych, zapewniający walidację buforowanych zasobów. Serwer oblicza ETag jako sumę hash (MD5, SHA-256) lub numer wersji zasobu i zwraca go w odpowiedzi na żądanie GET. Klient zapisuje ETag wraz z danymi i przy ponownym żądaniu wysyła go w nagłówku If-None-Match. Jeśli zawartość zasobu się nie zmieniła, serwer odpowiada statusem 304 Not Modified bez treści odpowiedzi.
Dla aplikacji mobilnych ETag jest krytyczny, ponieważ zmniejsza ilość pobieranych danych. Przy każdym uruchomieniu lub synchronizacji aplikacja sprawdza aktualność zasobów żądaniem z If-None-Match — zamiast pełnego pobierania danych otrzymuje 304 i korzysta z lokalnej kopii. Według Google Chrome Team (2024), użycie ETag w mobilnych API redukuje średni rozmiar odpowiedzi o 87% dla list i o 94% dla pojedynczych obiektów.
ETag jest generowany po stronie serwera i może być zarówno deterministyczny (taki sam dla identycznej treści, co jest przydatne dla współdzielonych pamięci podręcznych), jak i unikalny dla każdej odpowiedzi (do ścisłej walidacji). W REST API przeznaczonych do synchronizacji mobilnej najczęściej stosuje się kombinację hasha treści i numeru wersji rekordu w bazie danych.
Silne ETag (strong ETag) — identyfikatory, które zmieniają się przy każdej zmianie zawartości, w tym nieznacznych (spacje, formatowanie). Format: "abc123def" (w cudzysłowie, bez prefiksu). Silne ETag gwarantują, że zasób nie zmienił się bajt po bajcie. Są wymagane dla żądań zakresowych (Range requests) i do weryfikacji integralności częściowych pobrań.
Słabe ETag (weak ETag) — identyfikatory z prefiksem W/, na przykład W/"abc123def". Dopuszczają, że zasób jest semantycznie równoważny, nawet jeśli reprezentacja bajtowa się różni. Słabe ETag są przydatne dla serwerów, które dynamicznie generują odpowiedzi z różnymi spacjami lub formatowaniem, ale o tym samym znaczeniu. Jednak słabe ETag nie obsługują żądań zakresowych.
Porównanie typów ETag:
| Cecha | Silny ETag | Słaby ETag |
|---|---|---|
| Format | "hash" | W/"hash" |
| Czułość | Bajt po bajcie | Semantyczna |
| Żądania zakresowe | Obsługiwane | Nieobsługiwane |
| Buforowanie CDN | Idealne | Ograniczone |
| Synchronizacja | Wysoka dokładność | Dopuszcza kolizje |
Last-Modified — nagłówek HTTP wskazujący datę i czas ostatniej modyfikacji zasobu. Klient wysyła go z powrotem w nagłówku If-Modified-Since. Last-Modified jest prostszy w implementacji (serwer potrzebuje tylko daty), ale ma fundamentalne ograniczenia: rozdzielczość jednej sekundy (dwie zmiany w ciągu jednej sekundy są nierozróżnialne) i niemożność określenia, czy zawartość zmieniła się przy tej samej dacie (na przykład po przywróceniu z kopii zapasowej).
ETag rozwiązuje te problemy: hash treści zmienia się przy każdej zmianie niezależnie od czasu. Dlatego nowoczesne REST API używają kombinacji obu nagłówków: ETag do precyzyjnej walidacji i Last-Modified do przybliżonej filtracji w CDN. Apache HTTP Server i Nginx domyślnie generują oba nagłówki dla plików statycznych.
Dla aplikacji mobilnych z synchronizacją ETag jest kluczowy, ponieważ umożliwia wykrywanie konfliktów edycji. Jeśli klient wysyła PUT z nagłówkiem If-Match: "etag", serwer odrzuca żądanie, jeśli zasób został zmieniony przez innego klienta (optymistyczne blokowanie). Last-Modified nie może zagwarantować takiej niezawodności ze względu na dokładność co do sekundy.
Rozważmy implementację kliencką ETag w aplikacji mobilnej w Kotlin z użyciem Retrofit i OkHttp. Przy każdym żądaniu GET klient zapisuje ETag z odpowiedzi, a przy następnym żądaniu wysyła go w nagłówku If-None-Match. Jeśli serwer zwróci 304, dane nie są pobierane ponownie.
Konfiguracja klienta OkHttp z buforowaniem 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}"))
}
}
}
Klient zapisuje ETag po udanej odpowiedzi 200 i wysyła go w nagłówku If-None-Match przy następnym żądaniu. Przy 304 klient wie, że lokalna wersja jest aktualna i nie marnuje ruchu na ponowne pobieranie danych. Ten wzorzec redukuje koszty sieciowe aplikacji mobilnej o 80–90% dla często żądanych zasobów.
ETag jest kluczowym mechanizmem optymalizacji synchronizacji aplikacji mobilnych z REST API. W standardowym schemacie synchronizacji klient najpierw żąda listy zasobów z walidacją ETag — jeśli żaden zasób się nie zmienił, serwer zwraca 304 i klient kończy synchronizację. Jeśli są zmiany, serwer zwraca tylko zmodyfikowane zasoby. Takie podejście nazywa się deltową synchronizacją i jest krytyczne dla urządzeń mobilnych z ograniczonym ruchem.
W scenariuszach z optymistycznym blokowaniem ETag służy do zapobiegania konfliktom Lost Update. Gdy klient wysyła PUT w celu aktualizacji zasobu, dołącza nagłówek If-Match: "etag". Jeśli ETag nie jest zgodny (inny klient już zmienił zasób), serwer odpowiada 412 Precondition Failed, a klient musi ponownie pobrać aktualną wersję i powtórzyć zmianę. To podejście zapewnia spójność danych bez blokad na poziomie bazy danych.
Dla systemów rozproszonych z trybem offline ETag jest używany w połączeniu z Conflict Resolution. Klient synchronizuje się, pobierając aktualne ETag dla wszystkich zasobów. Przy wysyłaniu zmian serwer sprawdza If-Match — jeśli ETag się nie zgadza, rejestrowany jest konflikt, który jest rozwiązywany według wybranej strategii (LWW, Merge). Według Postman API Report (2025), 67% produkcyjnych REST API dla aplikacji mobilnych używa ETag jako głównego mechanizmu walidacji wersji.
Często zadawane pytania
ETag — nagłówek odpowiedzi HTTP zawierający unikalny identyfikator wersji zasobu. Klient używa go do żądań warunkowych: jeśli zasób się nie zmienił, serwer zwraca 304 Not Modified bez treści odpowiedzi, oszczędzając ruch.
ETag używa hasha treści do dokładnego porównania. Last-Modified opiera się na dacie modyfikacji z dokładnością do sekundy. ETag jest bardziej niezawodny do wykrywania rzeczywistych zmian i obsługuje optymistyczne blokowanie przez If-Match.
Silne ETag (bez prefiksu) rozróżniają zasoby bajt po bajcie. Słabe ETag (z prefiksem W/) dopuszczają semantyczną równoważność. Silne są wymagane dla żądań zakresowych, słabe — dla dynamicznie generowanej treści.
ETag redukuje ruch o 80–90%: klient sprawdza aktualność wszystkich zasobów przez If-None-Match, pobierając tylko zmienione. Bez ETag klient pobierałby pełne dane przy każdej synchronizacji, marnując ruch i baterię.
Serwer oblicza ETag jako hash (MD5, SHA-256) treści odpowiedzi lub używa numeru wersji rekordu z bazy danych. W Spring Boot wystarczy adnotacja @Cacheable z etag = true. W Express.js — middleware etag jest domyślnie włączony.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również