ETag

ETag (Entity Tag) — HTTP-заголовок, содержащий идентификатор версии ресурса. Обычно это хеш содержимого или инкрементальный номер. Позволяет клиенту спросить «а версия всё та же?» и не качать заново, а серверу — предотвратить lost update при конкурентном изменении.

Как выглядит

HTTP/1.1 200 OK
ETag: "5d8c72a5edda3d7c6b4b2e6b4a2c8b1e"
Content-Length: 12345
...

Синтаксис — строка в кавычках. Может быть с префиксом W/ для weak-ETag:

ETag: W/"12345-abcdef"

Strong vs Weak

Strong ETagWeak ETag (W/"...")
ОзначаетБайт-в-байт одинаковый ресурс«Смыслово тот же», байты могут отличаться
ПримерSHA-256 контентаХеш только «важных» полей, без timestamps
Годен для Range-запросовДаНет
Годен для If-Match (защита от race)ДаНет (только If-None-Match)

Кэширование — If-None-Match

Клиент имеет закэшированную копию с ETag "abc". При следующем запросе:

GET /image.png HTTP/1.1
If-None-Match: "abc"

Сервер сравнивает свой текущий ETag с присланным:

Concurrency control — If-Match

Проблема lost update: два клиента загрузили ресурс с версией "v5", оба меняют, оба PUT. Второй PUT перезапишет первый.

PUT /doc HTTP/1.1
If-Match: "v5"

...

Сервер проверяет: текущий ETag всё ещё "v5"?

Это оптимистичная блокировка — не блокирует ресурс, но ловит конфликты.

ETag vs Last-Modified

Ещё один способ версионирования — заголовок Last-Modified: Wed, 15 Jul 2026 22:00:00 GMT. Проверяется через If-Modified-Since.

ETagLast-Modified
ГранулярностьЛюбая (даже байт)Секунда
Определяет «одинаковость»По содержимомуПо времени
Работает если mtime сбросилсяДаНет
Работает для CDN с многими нодамиДа (если детерминированный)Плохо (mtime разный)

Современные серверы шлют оба, клиенты могут использовать любой.

Как генерировать

Проблемы и подводные

ETag в реальных API

См. также