← Назад к вопросам

Кэшируется ли PUT запрос?

2.0 Middle🔥 201 комментариев
#Кэширование и Redis

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличный и очень тонкий вопрос, который затрагивает глубины HTTP-спецификации и практики реализации кэширования. Ответ неоднозначен и зависит от контекста.

Краткий ответ: Согласно HTTP/1.1 спецификации (RFC 7231), ответ на PUT-запрос по умолчанию не кэшируется. Однако сервер может явно разрешить его кэширование, отправив соответствующие заголовки (например, Cache-Control с max-age или Expires). На практике это делается крайне редко, так как несет риски и нарушает принципы идемпотентности и атомарности обновления.

Давайте разберем это подробно.

Что говорит спецификация HTTP?

Стандарт определяет кэшируемость методов на основе понятия «кэшируемый метод запроса». Ответ считается кэшируемым, если метод запроса кэшируем, а код состояния ответа кэшируем.

  • Кэшируемые методы: GET, HEAD, POST (с оговорками).
  • Некэшируемые методы: PUT, DELETE, CONNECT, OPTIONS, TRACE.

Таким образом, PUT изначально не предназначен для кэширования. Основная причина — семантика метода.

Семантические причины против кэширования PUT

PUT является идемпотентным методом, предназначенным для полной замены ресурса по указанному URI. Его ключевые характеристики объясняют, почему кэширование ответов на него — плохая идея:

  1. Идемпотентность: Многократное выполнение одного и того же PUT-запроса должно иметь тот же эффект, что и однократное. Если клиент получит закэшированный успешный ответ (например, 200 OK или 204 No Content) и повторно отправит запрос, это может привести к неожиданному поведению (например, если ресурс был изменен другим клиентом между первым и вторым запросом, второе выполнение все равно перезапишет его своими данными, но это уже будет новое состояние, а не подтверждение старого).

  2. Неявная инвалидация: Успешный PUT-запрос должен инвалидировать (помечать как невалидные) кэшированные представления связанных ресурсов. В частности:

    *   Кэш для `GET`-запроса к этому же URI должен быть инвалидирован, так как содержимое ресурса изменилось.
    *   Может быть инвалидирован кэш для `GET`-запросов к коллекциям или связанным ресурсам (если используется `Content-Location` или другие механизмы).

    Кэширование же самого ответа на `PUT` противоречит этой логике, создавая конфликт состояний.

Техническая возможность и заголовки

Несмотря на семантику, технически сервер может отправить кэшируемый ответ. Это контролируется заголовками:

PUT /api/users/123 HTTP/1.1
Host: example.com
Content-Type: application/json
If-Match: "abc123"

{"name": "John Doe"}
HTTP/1.1 200 OK
Cache-Control: max-age=3600, public
Content-Location: /api/users/123
ETag: "def456"
Content-Length: 0

Здесь Cache-Control: max-age=3600 явно разрешает кэшировать этот успешный ответ на час. Однако это крайне опасная практика:

  • Клиент или промежуточный прокси-кэш может вернуть этот ответ на следующий PUT-запрос, не выполнив его на сервере.
  • Это нарушит идемпотентность, так как состояние ресурса на сервере может быть уже другим.
  • Это сделает невозможной гарантию атомарности обновления.

Практика и реализация в современных фреймворках

В реальных приложениях (ASP.NET Core, Node.js/Express, Django и т.д.) ответы на PUT практически никогда намеренно не кэшируются.

  • Промежуточное ПО кэширования (например, ResponseCachingMiddleware в ASP.NET Core) обычно настроено на игнорирование некэшируемых методов.
  • API-шлюзы и CDN (Cloudflare, Akamai) по умолчанию не кэшируют ответы на PUT.
  • Браузеры также не кэшируют ответы на PUT.

Вместо этого, фокус смещается на инвалидацию кэша:

// Пример в ASP.NET Core: Успешный PUT инвалидирует кэш для GET этого ресурса
[HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> UpdateUser(int id, UpdateUserDto dto)
{
    // ... бизнес-логика обновления ...

    // 1. Инвалидируем кэш для конкретного ресурса
    Response.Headers.Append("Cache-Control", "no-cache, no-store, must-revalidate");
    // Или более сильно, если нужно очистить кэш для всех связанных ресурсов:
    // Response.Headers.Append("Cache-Control", "no-cache");

    // 2. Часто используют ETag/If-Match для оптимистичной блокировки,
    // что также является механизмом контроля актуальности, а не кэширования.
    var newEtag = GenerateETag(updatedUser);
    Response.Headers.ETag = newEtag;

    return NoContent(); // 204
}

Выводы

  1. По умолчанию — нет: PUT не является кэшируемым методом согласно HTTP, и ответы на него по умолчанию не кэшируются клиентами, прокси или серверами.
  2. Технически — возможно, но опасно: Сервер может явно разрешить кэширование через Cache-Control, Expires или аналогичные заголовки, но это противоречит семантике метода, нарушает идемпотентность и создает серьезные риски согласованности данных.
  3. Правильный подход — инвалидация: Основная роль кэширования при работе с PUT — это инвалидация существующих кэшированных копий (в первую очередь, GET-запросов к обновленному ресурсу), а не кэширование ответа на сам PUT.

Таким образом, при проектировании RESTful API или бэкенд-сервиса следует исходить из того, что ответы на PUT-запросы не кэшируются. Любые попытки изменить это поведение требуют глубокого понимания последствий для консистентности данных и должны быть чрезвычайно хорошо обоснованы.

Кэшируется ли PUT запрос? | PrepBro