Почему POST-запросы не кэшируются
Что проверяют
Знаете ли вы, как HTTP-кэш строит ключ и чем безопасные методы отличаются от небезопасных. Сильный ответ начинается с поправки: спецификация кэшировать POST не запрещает, отказываются реализации. Следом часто спрашивают, как кэшировать GraphQL, который ходит POST-запросами.
Как отвечать
Каркас из трёх причин:
- семантика: GET безопасный метод, он только читает. POST небезопасный и неидемпотентный, повтор может создать второй заказ. Отдать сохранённый ответ вместо реального запроса значит молча не выполнить действие;
- ключ кэша: браузеры, прокси и CDN строят ключ из метода и URL. Тело в ключ не входит, а у POST весь смысл в теле, и два разных POST на один адрес для кэша неотличимы;
- инвалидация: по RFC 9111 успешный небезопасный запрос обязан сбросить закэшированные ответы для своего URL. Для кэша POST это сигнал «данные изменились», а не источник ответа.
Формально RFC 9110 разрешает кэшировать ответ на POST, если в нём явно указана свежесть и Content-Location совпадает с адресом запроса, но браузеры и типовые CDN этим не пользуются. В Service Worker cache.put() для POST-запроса просто выбрасывает TypeError.
Что делать во фронтенде. Чтение ведите через GET, тогда работают Cache-Control, ETag и CDN. Для GraphQL есть persisted queries: вместо текста запроса уходит хэш, и такой запрос можно отправлять GET-ом. TanStack Query и Apollo кэшируют результат у себя по ключу, метод им неважен. После отправки формы применяйте Post/Redirect/Get, чтобы обновление страницы не отправляло форму повторно.
Где спотыкаются
Говорят «стандарт запрещает кэшировать POST». Это неверно, и интервьюер это знает.
Путают идемпотентность с безопасностью. PUT идемпотентен, но кэш его ответы тоже не хранит, потому что метод меняет состояние.
Объясняют всё заголовком Cache-Control: no-cache, который якобы есть у POST. Такого поведения по умолчанию нет, причина в семантике метода и в ключе кэша.