TATECHATLAS
◎ Русский
Web и API

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

Когда использовать If-None-Match для кеша, If-Match для изменений и как правильно обработать ответы 304 и 412.

В этом материале

ETag - валидатор конкретного представления ресурса. Клиент возвращает полученную метку в If-None-Match при чтении или в If-Match при изменении. Совпадение If-None-Match для GET или HEAD может дать ответ 304 Not Modified: ранее сохранённое тело можно использовать повторно. Невыполненное условие If-Match приводит к 412 Precondition Failed, после чего нужно получить актуальное представление и согласовать изменения. Эти механизмы решают разные задачи. Метка ETag не заменяет авторизацию и не означает, что содержимое одного пользователя разрешено отдавать другим из общего кеша.

Что обозначает ETag

ETag относится к выбранному представлению, а не просто к адресу страницы. Значение определяет сервер, а клиент воспринимает его как непрозрачный идентификатор. Метка не обязана быть хешем файла, временем изменения или номером версии. Если ответ зависит от языка или других заголовков запроса, это тоже важно. Перед настройкой проверки выясните, какое представление сохранено, каким запросом оно было получено и какая метка сопровождала именно этот ответ.

Сохраняйте метку без преобразований

Сохраняйте полученную метку полностью: с кавычками и возможным префиксом W/. В учебном ответе используется ETag: "version-a". Не удаляйте кавычки, не создавайте значение из локального времени и не пытайтесь сравнивать версии по названию. Сервер может использовать совершенно другую схему. Храните валидатор вместе с соответствующим ответом, а не одну общую метку для всех ресурсов. Особенно опасно сопоставлять тело одной языковой версии с меткой другой версии.

HTTP/1.1 200 OK
ETag: "version-a"
Cache-Control: private, no-cache
Content-Type: application/json

{"title": "Example"}

Проверяйте кеш через If-None-Match

Когда сохранённый ответ требует проверки актуальности, передайте его метку в If-None-Match при GET. Если выбранное представление совпадает, сервер может вернуть 304. Если оно изменилось, сервер отдаст обычный ответ с телом, часто 200 и новой меткой. Проверка уменьшает повторную передачу тела, но запрос к серверу всё равно нужен. Свежий кешированный ответ иногда можно использовать без такого запроса: это определяется правилами кеширования, а не наличием ETag само по себе.

GET /documents/42 HTTP/1.1
Host: example.com
If-None-Match: "version-a"

Правильно обрабатывайте ответ 304

Ответ 304 не содержит нового тела представления. Сохранённый документ нужно оставить, обновив соответствующие метаданные кеша по заголовкам ответа. Не записывайте пустую строку вместо документа только потому, что сетевой ответ пришёл без тела. Если приложение потеряло сохранённое содержимое, одного ответа 304 недостаточно для восстановления ресурса. Потребуется запрос, позволяющий получить тело заново. Связка адреса, варианта представления, тела и метки должна оставаться согласованной после каждого обновления кеша.

HTTP/1.1 304 Not Modified
ETag: "version-a"
Cache-Control: private, no-cache

Защищайте изменения через If-Match

При изменении отправляйте в If-Match валидатор той версии, которую действительно редактировал пользователь. Если другой участник уже изменил ресурс, сервер может отклонить условие ответом 412. Получите текущую версию и согласуйте различия вместо слепого повторения записи. Сервер должен корректно проверять условие вместе с операцией изменения. Одно лишь отображение ETag в интерфейсе не защищает от потери чужой правки: нужна работающая проверка на стороне сервиса, который сохраняет данные.

PUT /documents/42 HTTP/1.1
Host: example.com
If-Match: "version-a"
Content-Type: application/json
Content-Length: 21

{"title": "Updated!"}

Различайте сильные и слабые валидаторы

Сильный валидатор означает побайтовую эквивалентность представлений. Слабая метка с W/ допускает более слабое соответствие. If-None-Match использует слабое сравнение, подходящее для проверки кеша. If-Match использует сильное сравнение, поэтому слабая метка не становится подходящим сильным валидатором для изменения. Нельзя просто удалить W/ и считать проблему решённой. Если сервис предоставляет только слабые метки, выбирайте поддерживаемый им способ контроля изменений и не предполагайте взаимозаменяемость всех значений ETag.

Учитывайте правила кеширования

ETag не заменяет Cache-Control и Vary. Первый задаёт правила кеширования, второй сообщает о заголовках запроса, влияющих на выбор представления. Значение no-cache допускает хранение, но требует проверки перед повторным использованием; no-store запрещает хранить ответ. Для персональных данных важна также политика частного и общего кеша. Сначала определите, кто может сохранять ответ и когда может его использовать. Добавление валидатора к неопределённой политике кеша само по себе эти вопросы не решает.

Разбирайте проблему по шагам

Посмотрите статус первого ответа, ETag, Cache-Control и Vary. Затем сравните условное чтение неизменившегося ресурса и изменённого представления. Для конфликта записи проверьте, какая метка отправлена в If-Match и какой статус вернулся. В журналах обычно достаточно идентификаторов ресурса и статусов, без чувствительного тела. HTTP-блоки здесь являются учебными обменами, а не отчётом о выполненном сетевом тесте. Такая последовательность помогает отличить потерянное тело кеша, неверный валидатор и отсутствие проверки условий сервером.

Что проверить

  • Храните ETag вместе с тем представлением, для которого он получен.
  • При 304 сохраняйте прежнее тело, а не заменяйте его пустым ответом.
  • Обрабатывайте 412 как конфликт изменения и перечитывайте ресурс перед повтором.
  • Проверяйте Cache-Control, Vary и тип валидатора вместе.

Условные запросы требуют корректного хранения у клиента и проверки на сервере. Они не обеспечивают авторизацию, конфиденциальность или полную политику согласования правок. Примеры объясняют HTTP; экономия трафика не измерялась.

Источники

  1. MDN: HTTP conditional requests ↗
  2. MDN: ETag ↗
  3. RFC 9110: HTTP semantics and conditional requests ↗
  4. RFC 9111: HTTP caching ↗
Наверх ↑