Можно ли кэшировать методы POST в HTTP?

С очень простой семантикой кэширования: если параметры совпадают (и URL тот же, конечно), то это хит. Это возможно? Рекомендуемые?


person flybywire    schedule 09.03.2009    source источник


Ответы (9)


Соответствующий RFC 2616 в разделе 9.5 (POST) позволяет кэшировать ответ в сообщение POST, если вы используете соответствующие заголовки.

Ответы на этот метод не кэшируются, если ответ не включает соответствующие поля заголовка Cache-Control или Expires. Тем не менее, ответ 303 (см. Другое) может быть использован для указания агенту пользователя получить кэшируемый ресурс.

Обратите внимание, что в разделе 13 (Кэширование в HTTP) того же RFC прямо указано, что кэш должен аннулировать соответствующий объект после запроса POST.

Некоторые методы HTTP ДОЛЖНЫ приводить к тому, что кеш делает объект недействительным. Это либо объект, на который ссылается Request-URI, либо заголовки Location или Content-Location (если они есть). Эти методы:

  - PUT
  - DELETE
  - POST

Мне непонятно, как эти спецификации могут обеспечить осмысленное кэширование.

Это также отражено и дополнительно разъяснено в RFC 7231 (раздел 4.3.3.), который отменяет RFC. 2616.

Ответы на запросы POST кэшируются только в том случае, если они содержат
явную информацию о свежести (см. раздел 4.2.1 [RFC7234]).
Однако кэширование POST широко не применяется. В случаях, когда исходный сервер желает, чтобы клиент имел возможность кэшировать результат POST таким образом, чтобы его можно было повторно использовать более поздним GET, исходный сервер МОЖЕТ отправить ответ 200 (OK), содержащий результат и Content-Location. поле заголовка, которое имеет то же значение, что и эффективный URI запроса POST (раздел 3.1.4.2).

В соответствии с этим результат кэшированного POST (если такая возможность указана сервером) может быть впоследствии использован как результат GET-запроса для того же URI.

person Diomidis Spinellis    schedule 09.03.2009
comment
Этот раздел относится к промежуточному кэшу (например, кэширующему прокси-серверу), а не к исходному серверу. - person David Z; 09.03.2009
comment
Верно, но их можно интерпретировать и в этом контексте (и они имеют смысл). - person Grey Panther; 09.03.2009
comment
Исходный сервер является посредником между HTTP и приложением, обрабатывающим запросы POST. Приложение выходит за пределы HTTP и может делать все, что ему заблагорассудится. Если кэширование имеет смысл для конкретного POST-запроса, его можно кэшировать так же, как ОС может кэшировать запросы к диску. - person Diomidis Spinellis; 09.03.2009
comment
Это то, что я имел в виду, что приложение, работающее на исходном сервере, не связано ограничениями HTTP на кеширование. - person David Z; 09.03.2009
comment
Диомидис, ваше утверждение о том, что кеширование POST-запросов не будет HTTP, неверно. Подробности см. в ответе reBoot. Не очень полезно, чтобы неправильный ответ отображался вверху, но именно так работает демократия. Если вы согласны с reBoot, было бы неплохо, если бы вы поправили свой ответ. - person Evgeniy Berezovsky; 11.08.2011
comment
Евгений, можем ли мы согласиться с тем, что а) POST должен аннулировать кешированный объект (согласно разделу 13.10), чтобы, например. последующий GET должен получить исходную копию и б) что ответ POST может быть кэширован (согласно разделу 9.5), так что, например. последующий POST может получить тот же ответ? - person Diomidis Spinellis; 15.08.2011
comment
Диомидис, мы, вероятно, можем согласиться со многими вещами, но я явно критиковал утверждение «если бы вы кешировали, это был бы не HTTP», поскольку спецификация HTTP говорит (хотя и сформулирована в обратном порядке) «кэширование в порядке, если используются соответствующие заголовки» . - person Evgeniy Berezovsky; 19.08.2011
comment
Евгений, я изменил запись, как я и предлагал. Однако я не вижу, как это будет работать на практике. - person Diomidis Spinellis; 27.08.2011
comment
Спасибо, Диомидис, я ценю это. - person Evgeniy Berezovsky; 31.08.2011
comment
Это выясняется HTTPbis; см. mnot.net/blog/2012/09/24/caching_POST. для резюме. - person Mark Nottingham; 23.09.2012
comment
Разъяснение Марка Ноттингема с немного измененной формулировкой теперь стандартизировано как RFC 7231 который отменяет RFC 2616. - person gmk57; 27.05.2020
comment
Да или нет????? - person Time Killer; 09.03.2021
comment
Да, при определенных условиях, как указано в ответе. - person Diomidis Spinellis; 10.03.2021

Согласно RFC 2616, раздел 9.5:

«Ответы на метод POST не кэшируются, ЕСЛИ ответ не включает соответствующие поля заголовка Cache-Control или Expires».

Итак, ДА, вы можете кэшировать ответ на запрос POST, но только если он поступает с соответствующими заголовками. В большинстве случаев вы не хотите кэшировать ответ. Но в некоторых случаях, например, если вы не сохраняете данные на сервере, это вполне уместно.

Обратите внимание, что многие браузеры, включая текущий Firefox 3.0.10, не будут кэшировать ответ POST независимо от заголовков. IE в этом плане ведет себя более шустро.

Теперь я хочу прояснить некоторую путаницу в отношении RFC 2616 S. 13.10. Метод POST для URI не «аннулирует ресурс для кэширования», как некоторые здесь заявляют. Это делает ранее кэшированную версию этого URI устаревшей, даже если ее заголовки управления кэшем указывают на свежесть более длительного времени.

person Community    schedule 06.05.2009
comment
+1 reBoot, спасибо за объяснение проблемы с заголовками, а также за исправление ошибочных утверждений относительно 13.10. Удивительно, что эти неправильные ответы получили так много голосов. - person Evgeniy Berezovsky; 09.08.2011
comment
В чем разница между аннулированием ресурса для кеширования и устаревшей кешированной версией URI? Вы говорите, что серверу разрешено кэшировать ответ POST, а клиентам - нет? - person Gili; 12.01.2012
comment
создание кешированной версии устаревшего URI применяется, когда вы используете один и тот же URI для запросов GET и POST. Если вы являетесь кешем между клиентом и сервером, вы видите GET /foo и кэшируете ответ. Затем вы видите POST /foo, тогда вам требуется аннулировать кешированный ответ от GET /foo, даже если ответ POST не включает заголовки управления кешем, поскольку они являются одним и тем же URI, таким образом следующий GET /foo должен будет пройти повторную проверку, даже если исходные заголовки указывали, что кеш все еще будет активен (если вы не видели запрос POST /foo) - person Stephen Connolly; 28.09.2018
comment
But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.. В чем тогда вообще смысл такого POST API? - person Siddhartha; 07.06.2020
comment
Итак, каковы соответствующие заголовки??? - person Time Killer; 09.03.2021

В целом:

По сути, POST не является идемпотентной операцией. Таким образом, вы не можете использовать его для кэширования. GET должна быть идемпотентной операцией, поэтому она обычно используется для кэширования.

См. раздел 9.1 HTTP 1.1 RFC 2616 S. 9.1.

Кроме семантики метода GET:

Сам метод POST семантически предназначен для публикации чего-либо в ресурсе. POST нельзя кэшировать, потому что если вы делаете что-то один раз, два раза или три раза, то вы каждый раз изменяете ресурс сервера. Каждый запрос имеет значение и должен быть доставлен на сервер.

Сам метод PUT семантически предназначен для размещения или создания ресурса. Это идемпотентная операция, но она не будет использоваться для кэширования, поскольку за это время могло произойти DELETE.

Сам метод DELETE семантически предназначен для удаления ресурса. Это идемпотентная операция, но она не будет использоваться для кэширования, поскольку за это время могла произойти PUT.

Что касается кэширования на стороне клиента:

Веб-браузер всегда будет пересылать ваш запрос, даже если у него есть ответ от предыдущей операции POST. Например, вы можете отправлять электронные письма с помощью Gmail с разницей в пару дней. У них может быть одна и та же тема и тело, но оба письма должны быть отправлены.

Относительно кеширования прокси:

Прокси-сервер HTTP, который пересылает ваше сообщение на сервер, никогда не будет кэшировать ничего, кроме запроса GET или HEAD.

Относительно кэширования сервера:

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

Объявление ресурса недействительным:

Проверка HTTP 1.1 RFC 2616 S. 13.10 показывает, что метод POST должен аннулировать ресурс для кэширования.

person Brian R. Bondy    schedule 09.03.2009
comment
В основном POST не является идемпотентной операцией. Таким образом, вы не можете использовать его для кэширования. Это просто неправильно, и в этом нет смысла, подробности см. В ответе reBoot. К сожалению, я пока не могу поставить минус, а то бы поставил. - person Evgeniy Berezovsky; 11.08.2011
comment
Евгений: Я поменял не на не может. - person Brian R. Bondy; 11.08.2011
comment
Спасибо, Брайан, так лучше. Моя проблема с вашим POST не idemp. -› не может быть кэшировано, хотя было - и я недостаточно ясно выразился - даже если операция не является идемпотентной, это не означает, что она не кэшируется. Я предполагаю, что вопрос в том, смотрите ли вы на это с точки зрения сервера, который предлагает данные и знает их семантику, или вы смотрите на это с принимающей стороны (будь то кеширующий прокси и т. д. или клиент) . Если это клиент/прокси pov, то я полностью согласен с вашим постом. Если это сервер pov, если сервер говорит: клиент может кэшировать, то клиент может кэшировать. - person Evgeniy Berezovsky; 13.08.2011
comment
Юджин: Если имеет значение, вызывается ли он один раз или 5 раз, например, если вы отправляете сообщение в список, то вы хотите, чтобы этот вызов попал на сервер 5 раз, верно? И вы не хотите кэшировать его, чтобы он не попал на сервер, верно? Потому что есть побочные эффекты, которые важны. - person Brian R. Bondy; 13.08.2011
comment
[продолжение] Я, однако, не решил, действительно ли сервер должен отправлять заголовок, разрешающий кеширование, ТОЛЬКО если операция является идемпотентной. Хотя, думаю, в этом есть смысл. [только что увидел ваш ответ]: Согласен, так что, думаю, я решил: сервер должен сигнализировать о кешировании только в случае идемпотентности - и это также может быть POST, особенно учитывая необходимость X-HTTP-Method-Override в некоторые случаи. - person Evgeniy Berezovsky; 13.08.2011
comment
ОП спросил, возможно ли это, а не следует ли это делать, и спецификация HTTP (как указано в других ответах) действительно указывает, что сервер может возвращать заголовки кеша, чтобы указать, что ответ может быть кеширован. Остальные детали в вашем ответе хороши. - person perfectionist; 10.11.2016
comment
REST — POST не idemp. -› кэшировать нельзя, HTTP - POST можно кэшировать. Выбирайте в зависимости от того, придерживаетесь ли вы REST или просто используете HTTP в качестве протокола обмена данными. Оба правы в своих областях. - person skryvets; 03.07.2020

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

Ответы представляют собой запутанную смесь того, как должно работать кэширование, как кэширование работает в соответствии с RFC, как кэширование должно работать в соответствии с RFC и как кэширование работает на практике. Давайте начнем с RFC, пройдем демонстрацию того, как на самом деле работает браузер, а затем поговорим о CDN, GraphQL и других проблемных областях.

RFC 2616

Согласно RFC, запросы POST должны аннулировать кеш:

13.10 Invalidation After Updates or Deletions

..

Some HTTP methods MUST cause a cache to invalidate an entity. This is
either the entity referred to by the Request-URI, or by the Location
or Content-Location headers (if present). These methods are:
  - PUT
  - DELETE
  - POST

Этот язык предполагает, что запросы POST не кэшируются, но это не так (в данном случае). Кэш становится недействительным только для ранее сохраненных данных. RFC (кажется) явно разъясняет, что да, вы можете кэшировать POST запросы:

9.5 POST

..

Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields. However,
the 303 (See Other) response can be used to direct the user agent to
retrieve a cacheable resource.

Несмотря на этот язык, установка Cache-Control не должна кэшировать последующие POST запросы к тому же ресурсу. POST запросы должны быть отправлены на сервер:

13.11 Write-Through Mandatory

..

All methods that might be expected to cause modifications to the
origin server's resources MUST be written through to the origin
server. This currently includes all methods except for GET and HEAD.
A cache MUST NOT reply to such a request from a client before having
transmitted the request to the inbound server, and having received a
corresponding response from the inbound server. This does not prevent
a proxy cache from sending a 100 (Continue) response before the
inbound server has sent its final reply.

Как это имеет смысл? Ну, вы не кешируете запрос POST, вы кешируете ресурс.

Тело ответа POST можно кэшировать только для последующих запросов GET к тому же ресурсу. Установите заголовок Location или Content-Location в ответе POST, чтобы сообщить, какой ресурс представляет тело. Таким образом, единственный технически допустимый способ кэширования POST-запроса — это последующие GET-запросы к тому же ресурсу.

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

  • «да, RFC позволяет вам кэшировать POST-запросы для последующих GET к тому же ресурсу»
  • «нет, RFC не позволяет вам кэшировать запросы POST для последующих POST, потому что POST не является идемпотентным и должен быть записан на сервер»

Хотя RFC позволяет кэшировать запросы к одному и тому же ресурсу, на практике браузеры и CDN не реализуют такое поведение и не позволяют кэшировать POST-запросы.

Источники:

Демонстрация поведения браузера

Учитывая следующий пример приложения JavaScript (index.js):

const express = require('express')
const app = express()

let count = 0

app
    .get('/asdf', (req, res) => {
        count++
        const msg = `count is ${count}`
        console.log(msg)
        res
            .set('Access-Control-Allow-Origin', '*')
            .set('Cache-Control', 'public, max-age=30')
            .send(msg)
    })
    .post('/asdf', (req, res) => {
        count++
        const msg = `count is ${count}`
        console.log(msg)
        res
            .set('Access-Control-Allow-Origin', '*')
            .set('Cache-Control', 'public, max-age=30')
            .set('Content-Location', 'http://localhost:3000/asdf')
            .set('Location', 'http://localhost:3000/asdf')
            .status(201)
            .send(msg)
    })
    .set('etag', false)
    .disable('x-powered-by')
    .listen(3000, () => {
        console.log('Example app listening on port 3000!')
    })

И учитывая следующий пример веб-страницы (index.html):

<!DOCTYPE html>
<html>

<head>
    <script>
        async function getRequest() {
            const response = await fetch('http://localhost:3000/asdf')
            const text = await response.text()
            alert(text)
        }
        async function postRequest(message) {
            const response = await fetch(
                'http://localhost:3000/asdf',
                {
                    method: 'post',
                    body: { message },
                }
            )
            const text = await response.text()
            alert(text)
        }
    </script>
</head>

<body>
    <button onclick="getRequest()">Trigger GET request</button>
    <br />
    <button onclick="postRequest('trigger1')">Trigger POST request (body 1)</button>
    <br />
    <button onclick="postRequest('trigger2')">Trigger POST request (body 2)</button>
</body>

</html>

Установите NodeJS, Express и запустите приложение JavaScript. Откройте веб-страницу в браузере. Попробуйте несколько разных сценариев, чтобы проверить поведение браузера:

  • При нажатии «Запустить запрос GET» каждый раз отображается одно и то же «счетчик» (работает кэширование HTTP).
  • Нажатие «Запустить запрос POST» каждый раз запускает другой счетчик (кеширование HTTP для POST не работает).
  • Щелчок «Инициировать запрос GET», «Инициировать запрос POST» и «Инициировать запрос GET» показывает, что запрос POST делает недействительным кеш запроса GET.
  • Нажав «Запустить запрос POST», а затем «Запустить запрос GET», вы увидите, что браузеры не будут кэшировать запросы POST для последующих запросов GET, даже если это разрешено RFC.

Это показывает, что, хотя вы можете установить заголовки ответов Cache-Control и Content-Location, нет никакого способа заставить браузер кэшировать запрос HTTP POST.

Должен ли я следовать RFC?

Поведение браузера не настраивается, но если вы не браузер, вы не обязательно связаны правилами RFC.

Если вы пишете код приложения, ничто не мешает вам явно кэшировать POST-запросы (псевдокод):

if (cache.get('hello')) {
  return cache.get('hello')
} else {
  response = post(url = 'http://somewebsite/hello', request_body = 'world')
  cache.put('hello', response.body)
  return response.body
}

CDN, прокси и шлюзы также не обязательно должны следовать RFC. Например, если вы используете Fastly в качестве CDN, Fastly позволяет вам писать пользовательский VCL для кеширования POST-запросов.

Должен ли я кэшировать POST-запросы?

Должен ли ваш POST-запрос кэшироваться или нет, зависит от контекста.

Например, вы можете запросить Elasticsearch или GraphQL с помощью POST, если ваш базовый запрос является идемпотентным. В этих случаях может иметь или не иметь смысл кэшировать ответ в зависимости от варианта использования.

В RESTful API запросы POST обычно создают ресурс и не должны кэшироваться. Это также понимание RFC о том, что POST не является идемпотентной операцией.

ГрафQL

Если вы используете GraphQL и вам требуется кэширование HTTP в CDN и браузерах, рассмотрите возможность отправки запросов с использованием метод GET соответствует вашим требованиям вместо POST. Обратите внимание, что разные браузеры и CDN могут иметь разные ограничения длины URI, но списки безопасных операций (белый список запросов), рекомендуемый для внешних производственных приложений GraphQL, могут сокращать URI.

person timrs2998    schedule 02.01.2020

Если вы кэшируете ответ POST, он должен направляться веб-приложению. Это то, что подразумевается под «Ответы на этот метод не кэшируются, если ответ не включает соответствующие поля заголовка Cache-Control или Expires».

Можно с уверенностью предположить, что приложение, которое знает, являются ли результаты POST идемпотентными, решает, присоединять ли необходимые и правильные заголовки управления кешем. Если присутствуют заголовки, предлагающие кэширование, приложение сообщает вам, что POST на самом деле является супер-GET; что использование POST требовалось только из-за количества ненужных и нерелевантных (для использования URI в качестве ключа кэша) данных, необходимых для выполнения идемпотентной операции.

При таком предположении следующие GET могут быть обслужены из кеша.

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

Тем не менее, каждый POST, попадающий в кеш, требует проверки с использованием условных заголовков. Это необходимо для обновления содержимого кеша, чтобы результаты POST не отражались в ответах на запросы до тех пор, пока не истечет время жизни объекта.

person JohnS    schedule 16.10.2015

Марк Ноттингем проанализировал, когда можно кэшировать ответ POST. Обратите внимание, что последующие запросы, которые хотят воспользоваться преимуществами кэширования, должны быть запросами GET или HEAD. См. также https://tools.ietf.org/html/rfc7231#section-4.3.3.

POST не имеют дело с представлениями идентифицированного состояния, 99 раз из 100. Однако есть один случай, когда это происходит; когда сервер изо всех сил пытается сказать, что этот ответ POST является представлением его URI, устанавливая заголовок Content-Location, который совпадает с URI запроса. Когда это происходит, ответ POST ничем не отличается от ответа GET на тот же URI; его можно кэшировать и использовать повторно, но только для будущих запросов GET.

https://www.mnot.net/blog/2012/09/24/caching_POST.

person dschulten    schedule 07.07.2014

Если это что-то, что на самом деле не изменяет данные на вашем сайте, это должен быть запрос GET. Даже если это форма, вы все равно можете установить ее как запрос на получение. Хотя, как отмечают другие, вы можете кэшировать результаты POST, это не будет иметь семантического смысла, потому что POST по определению изменяет данные.

person Kibbee    schedule 09.03.2009
comment
Запрос POST может не изменять какие-либо данные, которые используются для создания страницы ответа, и в этом случае имеет смысл кэшировать ответ. - person David Z; 09.03.2009
comment
Дэвид З.: Конечно, если POST изменяет данные, ответ должен указывать на успех или неудачу. Точно не требуется, но я не могу представить ситуацию, когда POST изменит данные, а ответ будет статическим. - person Morvael; 25.10.2013
comment
Если данные параметра слишком длинные, запрос GET не будет работать со всеми серверами, поэтому необходим POST, особенно если источник должен работать на серверах, которые не настроены автором кода. - person Gogowitsch; 06.04.2015
comment
@Gogowitsch очень верно, вы столкнетесь с кодом ошибки 414 - stackoverflow.com/a/2891598/792238 - person Siddhartha; 07.06.2020

С firefox 27.0 и httpfox 19 мая 2014 года я увидел одну строку: 00:03:58.777 0,488 657 (393) POST (Cache) text/html https://users.jackiszhp.info/S4UP

Понятно, что ответ почтового метода кешируется, и он тоже в https. Невероятный!

person user1462586    schedule 19.05.2014

POST используется в Ajax с отслеживанием состояния. Возврат кэшированного ответа для POST отключает канал связи и побочные эффекты получения сообщения. Это очень очень плохо. Это также настоящая боль, чтобы выследить. Настоятельно рекомендуется против.

Тривиальным примером может быть сообщение, побочным эффектом которого является выплата вашей зарплаты в размере 10 000 долларов США за текущую неделю. Вы НЕ хотите получить "Хорошо, все прошло!" предыдущая страница, которая была кэширована на прошлой неделе. Другие, более сложные случаи из реальной жизни вызывают такое же веселье.

person DragonLord    schedule 11.06.2013
comment
Не совсем ответ - POST используется для самых разных вещей, и иногда есть веские причины желать кэшировать ответ. - person Alexei Levenkov; 22.09.2017