HTTPでPOSTメソッドをキャッシュすることは可能ですか?


152

非常に単純なキャッシングセマンティクスの場合:パラメーターが同じであれば(もちろん、URLも同じであれば)、それはヒットです。それは可能ですか?おすすめですか?

回答:


93

セクション9.5(POST)の対応するRFC 2616では、適切なヘッダーを使用する場合、POSTメッセージへの応答をキャッシュできます。

このメソッドへの応答は、応答に適切なCache-ControlまたはExpiresヘッダーフィールドが含まれていない限り、キャッシュできません。ただし、303(その他を参照)応答を使用して、ユーザーエージェントにキャッシュ可能なリソースを取得するように指示できます。

注同じRFCが(HTTPでのキャッシング)部13で明示的に述べていることをキャッシュがPOSTの後に、対応するエンティティを無効にしなければならないことを要求

一部のHTTPメソッドは、キャッシュによってエンティティを無効にする必要があります。これは、Request-URI、またはLocationまたはContent-Locationヘッダー(存在する場合)によって参照されるエンティティです。これらのメソッドは次のとおりです。

  - PUT
  - DELETE
  - POST

これらの仕様が意味のあるキャッシングをどのように可能にするかは、私には明らかではありません。

これは、RFC 2616を廃止するRFC 7231(セクション4.3.3)にも反映され、さらに明確になっています

POST要求への応答は、
明示的な鮮度情報が含まれている場合にのみキャッシュ可能です([RFC7234]のセクション4.2.1を参照)。
ただし、POSTキャッシングは広く実装されていません。オリジンサーバーがクライアントがPOSTの結果を後のGETで再利用できる方法でキャッシュできることを望む場合、オリジンサーバーは結果とContent-Locationを含む200(OK)応答を送信してもよい(MAY) POSTの有効なリクエストURIと同じ値を持つヘッダーフィールド(セクション3.1.4.2)。

これによると、キャッシュされたPOSTの結果(この機能がサーバーによって示されている場合)は、同じURIに対するGET要求の結果として後で使用できます。


1
このセクションは、オリジンサーバーではなく、中間キャッシュ(キャッシングプロキシサーバーなど)に適用されます。
David Z

2
オリジンサーバーは、HTTPとPOSTリクエストを処理するアプリケーション間のブローカーです。アプリケーションはHTTP境界を超えており、好きなように実行できます。特定のPOSTリクエストに対してキャッシュが意味をなす場合、OSがディスクリクエストをキャッシュできる限り、キャッシュは自由にできます。
Diomidis Spinellis

2
POSTリクエストのキャッシュはHTTPではないというあなたの声明であるDiomidisは間違っています。詳細については、reBootの回答を参照してください。間違った答えを上に表示することはあまり役に立ちませんが、それが民主主義の仕組みです。reBootに同意する場合は、回答を修正していただければ幸いです。
Eugene Beresovsky

2
Eugene、a)POSTがキャッシュされたエンティティ(セクション13.10に従って)を無効にする必要があることに同意できますか?たとえば、後続のGETはfershコピーをフェッチする必要があり、b)POSTの応答は(セクション9.5に従って)キャッシュできるため、たとえば後続のPOSTは同じ応答を受信できますか?
Diomidis Spinellis 2011

3
これはHTTPbisによって明らかにされています。概要については、mnot.net / blog / 2012/09/24 / caching_POSTを参照してください。
マークノッティンガム

68

RFC 2616セクション9.5によれば、

「POSTメソッドへの応答はキャッシュできません。応答に適切なCache-ControlまたはExpiresヘッダーフィールドが含まれていない限り。」

したがって、はい、POSTリクエストの応答をキャッシュできますが、適切なヘッダーが付いている場合のみです。ほとんどの場合、応答をキャッシュする必要はありません。ただし、サーバーにデータを保存しない場合など、完全に適切な場合もあります。

ただし、現在のFirefox 3.0.10を含む多くのブラウザーは、ヘッダーに関係なくPOST応答をキャッシュしません。IEはこの点でよりスマートに動作します。

ここで、RFC 2616 S. 13.10に関する混乱を解消したいと思います。URIのPOSTメソッドは、「キャッシュのためにリソースを無効にする」ことはありません。そのキャッシュ制御ヘッダーがより長い期間の鮮度を示していたとしても、そのURIの以前にキャッシュされたバージョンを古くします。


2
ヘッダーの問題を説明し、13.10に関する誤ったステートメントを修正していただきありがとうございます。それらの間違った答えを驚くほど多くの賛成票を受け取った。
Eugene Beresovsky、2011

3
「キャッシュのためにリソースを無効にする」と「URIのキャッシュされたバージョンを古くする」の違いは何ですか?サーバーはPOST応答をキャッシュすることを許可されていますが、クライアントはキャッシュできないと言っていますか?
Gili

1
「キャッシュされたバージョンのURIを古くする」は、同じURIをGETPOSTリクエストに使用する場合に適用されます。あなたがクライアントとサーバーの間に座っているキャッシュであるなら、あなたはあなたが見てGET /foo、あなたは応答をキャッシュします。次に、同じURIであるために応答にキャッシュコントロールヘッダーが含まれていない場合でも、キャッシュされた応答を無効にする必要があるPOST /fooことがわかります。したがって、元のヘッダーがまだキャッシュであることを示している場合でも、次は再検証する必要があります。ライブ(リクエストを見ていなかった場合)GET /fooPOSTGET /fooPOST /foo
スティーブンコノリー

But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.。では、そもそもそのようなPOST APIのポイントは何ですか?
シッダールタ

33

全体:

基本的に、POSTはべき等の操作ではありません。したがって、キャッシュに使用することはできません。GETはべき等の操作である必要があるため、一般的にキャッシュに使用されます。

HTTP 1.1 RFC 2616 S. 9.1のセクション9.1を参照してください。

GETメソッドのセマンティクス以外:

POSTメソッド自体は、意味的にリソースに何かを投稿することを目的としています。POSTはキャッシュできません。何かを1回、2回、3回行うと、サーバーのリソースが毎回変更されるためです。各リクエストは重要であり、サーバーに配信する必要があります。

PUTメソッド自体は、意味的にはリソースを配置または作成するためのものです。これはべき等の操作ですが、その間にDELETEが発生した可能性があるため、キャッシュには使用されません。

DELETEメソッド自体は、意味的にはリソースを削除するためのものです。これはべき等演算ですが、その間にPUTが発生した可能性があるため、キャッシュには使用されません。

クライアント側のキャッシングについて:

Webブラウザーは、前のPOST操作からの応答がある場合でも、常に要求を転送します。たとえば、Gmailを使用して数日間隔でメールを送信できます。件名と本文は同じでもかまいませんが、両方のメールを送信する必要があります。

プロキシキャッシングについて:

サーバーにメッセージを転送するプロキシHTTPサーバーは、GETまたはHEADリクエスト以外は何もキャッシュしません。

サーバーのキャッシュについて:

デフォルトでは、サーバーはキャッシュをチェックしてPOSTリクエストを自動的に処理しません。ただし、もちろん、POST要求をアプリケーションまたはアドインに送信することができ、パラメーターが同じである場合に、そこから読み取る独自のキャッシュを持つことができます。

リソースの無効化:

HTTP 1.1 RFC 2616 S. 13.10を確認すると、POSTメソッドがリソースをキャッシュ用に無効化する必要があることが示されています。


9
「基本的にPOSTはべき等の操作ではないので、キャッシュに使用することはできません。」これは間違っています。実際には意味がありません。詳細については、reBootの回答を参照してください。残念ながら、私はまだ投票できません。
Eugene Beresovsky、2011

1
ユージーン:「ではない」を「しない」に変更しました。
ブライアンR.ボンディ

1
ブライアン、ありがとう。あなたの「POST not idemp。-> ca n't cant cached」に関する私の問題は、操作がべき等ではなく、キャッシュ可能ではないというわけではありませんが、それを十分に明確にしていませんでした。問題は、データを提供してそのセマンティクスを知っているサーバーの視点から見ているのか、それとも受信側から見ているのか(キャッシングプロキシなどまたはクライアント) 。それがクライアント/プロキシの視点である場合、私はあなたの投稿に完全に同意します。それがサーバーの視点である場合、サーバーが「クライアントはキャッシュできる」と言う場合、クライアントはキャッシュできます。
Eugene Beresovsky

1
Eugene:メッセージがリストに投稿されている場合など、1回呼び出されるか5回呼び出されるかによって違いが生じる場合、その呼び出しでサーバーに5回ヒットする必要がありますか?そして、それをキャッシュしたくないので、サーバーにヒットしませんか?重要な副作用があるからです。
ブライアンR.ボンディ

[続き]ただし、操作がべき等である場合にのみ、サーバーが実際にキャッシュを許可する有効期限ヘッダーを送信する必要があるかどうかは、私は決めていません。でも、それは理にかなっていると思います。[あなたの応答を見たところ]:同意したので、私は心に留めたと思います:サーバーは、べき等の場合にのみキャッシュ可能性を通知する必要があります。ある場合。
Eugene Beresovsky

6

POST応答をキャッシュする場合、それはWebアプリケーションの指示である必要があります。これは、「適切なCache-ControlまたはExpiresヘッダーフィールドが応答に含まれていない限り、このメソッドへの応答はキャッシュできない」という意味です。

POSTの結果がべき等であるかどうかを知っているアプリケーションが、必要かつ適切なキャッシュ制御ヘッダーを添付するかどうかを決定すると、安全に想定できます。キャッシングが許可されていることを示唆するヘッダーが存在する場合、アプリケーションはPOSTが実際にはスーパーGETであることを通知しています。POSTの使用が必要なのは、べき等演算を実行するために必要な不必要で無関係な(キャッシュキーとしてのURIの使用)データが大量にあるためです。

以下のGETは、この前提の下でキャッシュから提供できます。

キャッシュ可能なPOST応答とキャッシュ不可能なPOST応答を区別するために必要な正しいヘッダーを添付できないアプリケーションは、無効なキャッシュ結果のせいです。

つまり、キャッシュにヒットする各POSTには、条件付きヘッダーを使用した検証が必要です。これは、キャッシュコンテンツを更新して、オブジェクトの有効期限が切れるまでPOSTの結果がリクエストへの応答に反映されないようにするために必要です。


4

マーク・ノッティンガムは、POSTの応答をキャッシュすることがいつ可能かを分析しました。キャッシュを利用したい後続のリクエストは、GETまたはHEADリクエストでなければならないことに注意してください。httpセマンティクスも参照

POSTは、識別された状態の表現を処理しません。100のうちの99倍です。ただし、1つの場合があります。サーバーが、要求URIと同じContent-Locationヘッダーを設定することにより、このPOST応答がそのURIの表現であると言うのをやめたとき。その場合、POST応答は同じURIに対するGET応答と同じです。キャッシュして再利用できますが、将来のGETリクエストでのみ使用できます。

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


4

投稿リクエストをキャッシュできるかどうか疑問に思っている場合、その質問に対する回答を調べてみると、成功しない可能性があります。「キャッシュポストリクエスト」を検索すると、最初の結果はこの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リクエストでのみキャッシュできます。POSTレスポンスのLocationor Content-Locationヘッダーを設定して、本文が表すリソースを伝えます。したがって、POSTリクエストをキャッシュする唯一の技術的に有効な方法は、同じリソースへの後続のGETです。

正解は両方です。

  • 「はい、RFCでは、同じリソースへの後続のGETのPOSTリクエストをキャッシュできます。」
  • 「いいえ。POSTはべき等ではなく、サーバーに書き込む必要があるため、RFCでは後続の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!')
    })

また、次のWebページの例(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アプリケーションを起動します。ブラウザでWebページを開きます。ブラウザーの動作をテストするには、いくつかの異なるシナリオを試してください。

  • 「トリガーGETリクエスト」をクリックすると、毎回同じ「カウント」が表示されます(HTTPキャッシュは機能します)。
  • [トリガーPOSTリクエスト]をクリックすると、毎回異なるカウントがトリガーされます(POSTのHTTPキャッシュは機能しません)。
  • [トリガーGETリクエスト]、[トリガーPOSTリクエスト]、および[トリガーGETリクエスト]をクリックすると、POSTリクエストがGETリクエストのキャッシュを無効にします。
  • 「トリガーPOSTリクエスト」、「トリガーGETリクエスト」の順にクリックすると、RFCで許可されていても、ブラウザーは後続のGETリクエストのPOSTリクエストをキャッシュしません。

これは、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リクエストをキャッシュするかどうかは、コンテキストによって異なります。

たとえば、基になるクエリがべき等である場合、POSTを使用してElasticsearchまたはGraphQLにクエリを実行できます。これらのケースでは、ユースケースに応じて応答をキャッシュすることは意味がある場合とない場合があります。

RESTful APIでは、POSTリクエストは通常​​リソースを作成するため、キャッシュしないでください。これは、べき等演算ではないというPOSTに関するRFCの理解でもあります。

GraphQL

GraphQLを使用していて、CDNとブラウザー間でHTTPキャッシュが必要な場合は、GETメソッドを使用してクエリを送信することがPOSTではなく要件を満たすかどうかを検討してください。注意点として、ブラウザーやCDNによってURIの長さの制限が異なる可能性がありますが、外部向けのプロダクションGraphQLアプリのベストプラクティスとして、操作のセーフリスト(クエリのホワイトリスト)はURIを短縮できます。


3

それが実際にサイトのデータを変更しないものである場合は、GETリクエストである必要があります。フォームであっても、取得リクエストとして設定できます。他の人が指摘しているように、POSTの結果をキャッシュすることはできますが、POSTは定義上データを変更しているため、意味がありません。


POST要求は、応答ページの生成に使用されるデータを変更していない可能性があります。その場合、応答をキャッシュすることは理にかなっています。
David Z

David Z:確かに、POSTがデータを変更している場合、応答は成功/失敗の何らかの指標を与えるはずです。厳密には必要ありませんが、POSTがデータを変更し、応答が静的である状況は考えられません。
Morvael 2013年

6
パラメータデータが長すぎる場合、GETリクエストはすべてのサーバーで機能しないため、特にコード作成者が構成していないサーバーでソースを実行する必要がある場合は、POSTが必要です。
Gogowitsch 2015

-非常に真@Gogowitsch、あなたは414エラーコードに実行されますstackoverflow.com/a/2891598/792238
シッダールタ

-2

firefox 27.0とhttpfoxを使用して、2014年5月19日に次の1行を確認しました:00:03:58.777 0.488 657(393)POST(キャッシュ)text / html https://users.jackiszhp.info/S4UP

明らかに、postメソッドの応答はキャッシュされ、httpsにも含まれます。信じられない!


-3

POSTはステートフルAjaxで使用されます。POSTに対してキャッシュされた応答を返すと、通信チャネルおよびメッセージ受信の副作用が無効になります。これは非常に悪いです。追跡するのも大変です。に対して強くお勧めします。

自明な例としては、副作用として、今週、給与を$ 10,000支払うというメッセージがあります。あなたは「OK、それは通り抜けた!」を取得したくない 先週キャッシュされたページバック。その他の、より複雑な実世界のケースは、同様の陽気さをもたらします。


3
本当に答えではありません-POSTはあらゆる種類のものに使用され、時には応答をキャッシュしたい正当な理由があります。
Alexei Levenkov 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.