ETag (Entity Tag) — nagłówek HTTP, który przypisuje unikalny identyfikator wersji zasobu na serwerze, umożliwiając klientowi efektywne sprawdzanie aktualności buforowanych danych. Przy ponownym żądaniu przeglądarka lub aplikacja wysyła zapisany ETag, a serwer porównuje go z bieżącym: w przypadku zgodności zwracany jest status 304 Not Modified bez treści odpowiedzi. Według RFC 7232 (IETF, 2014), żądania warunkowe z ETag zmniejszają ilość przesyłanych danych nawet o 95% dla często żądanych zasobów. Czyni to nagłówek krytycznie ważnym dla wydajności aplikacji mobilnych.
Najważniejsze
ETag (Entity Tag) — to nagłówek odpowiedzi HTTP zawierający unikalny identyfikator konkretnej wersji zasobu. Serwer oblicza ETag na podstawie zawartości pliku, jego metadanych lub numeru rewizji i przekazuje go klientowi w odpowiedzi na żądanie GET. Klient zapisuje ten identyfikator i przy kolejnych żądaniach do tego samego zasobu wysyła go w nagłówku If-None-Match. Jeśli zasób się nie zmienił, serwer odpowiada 304 Not Modified, a klient korzysta ze swojej buforowanej kopii.
Format ETag jest określony w RFC 7232 jako ciąg w cudzysłowie: "33a64df551425fcc55e4d42a148795d9f25f89d4". Wartość może być skrótem SHA-1 zawartości pliku, przyrostowym numerem wersji, kombinacją inode-numer-czas dla plików statycznych lub dowolnym tokenem generowanym przez serwer. Jedynym wymaganiem jest, aby wartość zmieniała się przy każdej zmianie zasobu i nie zmieniała się, jeśli zasób pozostał taki sam.
ETag należy do mechanizmów żądań warunkowych (conditional requests) — jednej z podstawowych optymalizacji protokołu HTTP. W przeciwieństwie do żądań bezwarunkowych, gdzie serwer zawsze zwraca pełną odpowiedź, żądanie warunkowe pozwala klientowi sprawdzić aktualność pamięci podręcznej bez ponownego pobierania danych. Według danych HTTP Archive (2025), około 40% wszystkich odpowiedzi HTTP to 304 Not Modified dzięki prawidłowej konfiguracji ETag i Last-Modified.
ETag jest używany w REST API do optymalizacji ładowania kolekcji danych — jeśli lista obiektów się nie zmieniła, klient otrzymuje 304 bez przesyłania całego JSON. W plikach statycznych (CSS, JS, obrazy) ETag pozwala CDN i przeglądarkom efektywnie sprawdzać aktualność pamięci podręcznej. W aplikacjach mobilnych ETag jest krytyczny dla synchronizacji w tle: aplikacja sprawdza, czy dane na serwerze się zmieniły, i pobiera aktualizacje tylko w razie potrzeby. Oszczędza to przepustowość i baterię urządzenia.
Pełny cykl działania ETag składa się z czterech kroków. Serwer generuje ETag przy pierwszym żądaniu i zwraca go w nagłówku odpowiedzi. Klient zapisuje ETag wraz z buforowanym zasobem. Przy ponownym żądaniu klient wysyła nagłówek If-None-Match z wartością zapisanego ETag. Serwer porównuje otrzymaną wartość z bieżącym ETag zasobu: w przypadku zgodności zwraca 304 Not Modified z pustą treścią, w przypadku niezgodności — 200 OK z nowym zasobem i nowym ETag.
// Źądanie klienta z If-None-Match
GET /api/users HTTP/1.1
Host: example.com
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
// Odpowiedź serwera — zasób nie uległ zmianie
HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
W aplikacji mobilnej ten cykl może być zaimplementowany przez klient HTTP z obsługą buforowania. OkHttp na przykład automatycznie zarządza ETag przez CacheInterceptor: zapisuje ETag odpowiedzi i przy ponownym żądaniu dodaje If-None-Match. Po otrzymaniu 304 OkHttp zwraca buforowane dane. OkHttp obsługuje ETag bez dodatkowej konfiguracji — wystarczy włączyć pamięć podręczną przez OkHttpClient.Builder.cache().
Serwer może obliczać ETag na różne sposoby: przez skrót MD5 lub SHA zawartości, przez numer rewizji z bazy danych (np. updated_at z MySQL), przez kombinację inode + mtime + size dla plików statycznych (Nginx generuje ETag właśnie w ten sposób). Dla dynamicznych API najbardziej niezawodny jest skrót treści: jeśli odpowiedź JSON zmieniła choć jedno pole, ETag się zmieni. Jednak obliczanie skrótu przy każdym żądaniu obciąża procesor — dla systemów o wysokim obciążeniu lepiej używać przyrostowego numeru wersji.
RFC 7232 definiuje dwa typy ETag: silne (strong) i słabe (weak). Silny ETag oznacza, że dwie reprezentacje zasobu są identyczne bajt po bajcie — żaden bit się nie różni. Słaby ETag (prefiks W/) gwarantuje tylko równoważność semantyczną: zawartość może się różnić na poziomie serializacji (spacje, kolejność pól JSON), ale dane dla klienta są uznawane za takie same. Słabe ETag są oznaczane prefiksem W/, np. W/"1a2b3c".
Wybór typu ETag zależy od wymagań dotyczących dokładności porównania. Dla plików statycznych (CSS, JS, obrazy) preferowane są silne ETag — jeśli plik się zmienił, klient powinien otrzymać nową wersję. Dla dynamicznych API, gdzie ten sam JSON może być serializowany z różną kolejnością pól lub formatowaniem, słabe ETag dają więcej elastyczności: serwer generuje ETag na podstawie danych biznesowych, a nie reprezentacji ciągu znaków.
| Typ ETag | Format | Gwarancja | Zastosowanie |
|---|---|---|---|
| Strong (silny) | "skrót" | Identyczność bajt po bajcie | Pliki statyczne, zasoby binarne |
| Weak (słaby) | W/"skrót" | Równoważność semantyczna | JSON API, strony dynamiczne |
Ograniczenie słabych ETag: nie mogą być używane z żądaniami zakresowymi (Range requests). Jeśli klient żąda części pliku, serwer musi zwrócić silny ETag, aby zagwarantować, że fragment odpowiada pełnemu zasobowi. Słabe ETag nie zapewniają takiej gwarancji. W pozostałych scenariuszach słabe ETag są bezpieczne i zalecane dla API.
ETag i Last-Modified to dwa nagłówki HTTP dla żądań warunkowych, które często są używane razem. Last-Modified wskazuje datę ostatniej modyfikacji zasobu i działa z nagłówkiem If-Modified-Since. ETag dostarcza unikalny identyfikator wersji i działa z If-None-Match. Każdy ma swoje zalety i ograniczenia, a kombinacja daje maksymalną efektywność buforowania.
Last-Modified jest prostszy w implementacji — serwer automatycznie pobiera datę z systemu plików lub aktualizuje pole updated_at w bazie danych. Jednak data ma dokładność do sekundy, co jest niewystarczające dla zasobów zmieniających się kilka razy na sekundę. Ponadto Last-Modified nie rozróżnia różnych stanów: jeśli plik został nadpisany tą samą wersją, data się zmieniła, ale treść nie — klient przeładuje identyczne dane.
ETag jest dokładniejszy: zmienia się tylko przy rzeczywistej zmianie treści. Jeśli serwer przywrócił poprzednią wersję z kopii zapasowej, ETag się zmieni. Jeśli plik został nadpisany tymi samymi danymi — ETag pozostanie taki sam, a klient nie będzie przeładowywać. Wspólne używanie jest zalecane przez specyfikację HTTP: serwer zwraca oba nagłówki, klient wysyła If-None-Match i If-Modified-Since jednocześnie. Jeśli choć jeden nagłówek wskazuje zmianę — serwer zwraca nowy zasób.
Zgodnie ze specyfikacją, ETag ma priorytet nad Last-Modified. Jeśli serwer otrzymał If-None-Match, powinien sprawdzać tylko ETag, ignorując If-Modified-Since. Zapobiega to race condition: jeśli zasób zmienił się między wysłaniem przez klienta Last-Modified a sprawdzeniem na serwerze, ETag będzie świeższym wskaźnikiem. W praktyce serwery zwykle sprawdzają oba nagłówki, ale przy niezgodności wyników wygrywa ETag.
Konfiguracja ETag zależy od typu serwera. Nginx generuje ETag dla plików statycznych automatycznie na podstawie inode, mtime i rozmiaru. Apache używa mechanizmu FileETag. Dla aplikacji dynamicznych w Node.js, PHP, Python, Ruby ETag należy generować programowo — przez skrót odpowiedzi, numer wersji danych lub kombinację parametrów żądania.
func etagMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
// Generowanie ETag na podstawie danych
etag := generateETag(r.URL.Path)
w.Header().Set("ETag", etag)
// Sprawdzanie If-None-Match
if r.Header.Get("If-None-Match") == etag {
w.WriteHeader(http.StatusNotModified)
return
}
next.ServeHTTP(w, r)
})
}
Middleware w Go przechwytuje żądanie, generuje ETag dla żądanego URL (np. oblicza skrót danych z pamięci podręcznej lub bazy danych) i ustawia nagłówek odpowiedzi. Jeśli klient wysłał If-None-Match i jest on zgodny z bieżącym ETag, serwer zwraca 304 Not Modified natychmiast, bez wywoływania głównego handlera. W środowisku produkcyjnym warto dodać buforowanie obliczonych ETag według URL i parametrów w celu zmniejszenia obciążenia serwera.
W konfiguracji wieloserwerowej (round-robin lub anycast) ETag musi być taki sam na wszystkich węzłach dla tego samego zasobu. Jeśli ETag jest generowany na podstawie inode pliku, a strona jest uruchomiona na kilku serwerach, wartości będą się różnić. Rozwiązaniem jest użycie skrótu zawartości lub scentralizowanego przechowywania wersji (Redis, etcd). Drugi problem — kompresja gzip: Nginx zmienia ETag przy włączonej kompresji, co może powodować nadmiarowe 304. Wymagana jest konfiguracja gzip_vary on do synchronizacji ETag z skompresowaną treścią.
Często zadawane pytania
Tak, jeśli serwer wyraźnie temu nie zapobiegł. ETag nie musi być globalnie unikalny — jest unikalny w obrębie konkretnego URL. Dla plików statycznych kolizje są mało prawdopodobne przy użyciu skrótu SHA, ale dla samodzielnych generatorów możliwe są duplikaty.
ETag jest najbardziej efektywny dla zasobów, które są żądane wielokrotnie i rzadko się zmieniają: statyka, listy API, konfiguracje. Dla unikalnych stron, które są ładowane jeden raz (np. strona potwierdzenia zamówienia), ETag nie daje przewagi.
CDN uwzględnia ETag w żądaniach origin do sprawdzania aktualności pamięci podręcznej. Jeśli ETag zasobu na origin się zmienił, CDN pobiera nową wersję. Cloudflare i Fastly obsługują ETag jako standardowy mechanizm unieważniania pamięci podręcznej na poziomie origin.
RFC 7232 nie ogranicza długości ETag, ale serwery i proxy mogą obcinać lub ignorować zbyt długie wartości. Zaleca się używanie skrótu o długości 20–40 znaków lub kombinacji identyfikatora wersji z sumą kontrolną.
To nie są wzajemnie wykluczające się mechanizmy. Cache-Control określa politykę buforowania (jak długo przechowywać, komu wolno), a ETag to mechanizm walidacji buforowanego zasobu. Optymalna konfiguracja obejmuje oba nagłówki razem.
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ż