Est-il possible de mettre en cache les méthodes POST dans HTTP?

152

Avec une sémantique de mise en cache très simple: si les paramètres sont les mêmes (et l'URL est la même, bien sûr), alors c'est un succès. Est-ce possible? Conseillé?

flybywire
la source

Réponses:

93

La RFC 2616 correspondante dans la section 9.5 (POST) permet la mise en cache de la réponse à un message POST, si vous utilisez les en-têtes appropriés.

Les réponses à cette méthode ne peuvent pas être mises en cache, sauf si la réponse inclut les champs d'en-tête Cache-Control ou Expires appropriés. Cependant, la réponse 303 (Voir autre) peut être utilisée pour demander à l'agent utilisateur de récupérer une ressource pouvant être mise en cache.

Notez que le même RFC indique explicitement dans la section 13 (Caching in HTTP) qu'un cache doit invalider l'entité correspondante après une requête POST .

Certaines méthodes HTTP DOIVENT provoquer l'invalidation d'une entité par un cache. Il s'agit soit de l'entité référencée par Request-URI, soit par les en-têtes Location ou Content-Location (le cas échéant). Ces méthodes sont:

  - PUT
  - DELETE
  - POST

Je ne vois pas clairement comment ces spécifications peuvent permettre une mise en cache significative.

Cela est également reflété et clarifié dans la RFC 7231 (section 4.3.3.), Qui rend obsolète la RFC 2616.

Les réponses aux demandes POST ne peuvent être mises en cache que lorsqu'elles incluent
des informations de fraîcheur explicites (voir la section 4.2.1 de la [RFC7234]).
Cependant, la mise en cache POST n'est pas largement implémentée. Pour les cas où un serveur d'origine souhaite que le client puisse mettre en cache le résultat d'un POST d'une manière qui puisse être réutilisée par un GET ultérieur, le serveur d'origine PEUT envoyer une réponse 200 (OK) contenant le résultat et un Content-Location champ d'en-tête qui a la même valeur que l'URI de demande effective du POST (paragraphe 3.1.4.2).

Selon cela, le résultat d'un POST mis en cache (si cette capacité est indiquée par le serveur) peut être ultérieurement utilisé comme résultat d'une requête GET pour le même URI.

Diomidis Spinellis
la source
1
Cette section s'applique à un cache intermédiaire (comme un serveur proxy de mise en cache), pas au serveur d'origine.
David Z
2
Le serveur d'origine est un courtier entre HTTP et l'application qui gère les requêtes POST. L'application est au-delà des limites HTTP et elle peut faire tout ce qu'elle veut. Si la mise en cache a du sens pour une requête POST spécifique, la mise en cache est gratuite, autant que le système d'exploitation peut mettre en cache les requêtes disque.
Diomidis Spinellis
2
Diomidis, votre déclaration selon laquelle la mise en cache des requêtes POST ne serait pas HTTP, est fausse. Veuillez consulter la réponse de reBoot pour plus de détails. Ce n'est pas très utile d'avoir la mauvaise réponse au sommet, mais c'est ainsi que fonctionne la démocratie. Si vous êtes d'accord avec reBoot, ce serait bien si vous corrigiez votre réponse.
Eugene Beresovsky
2
Eugene, pouvons-nous convenir que a) POST devrait invalider l'entité mise en cache (par section 13.10), de sorte que par exemple, un GET ultérieur doit récupérer une copie fersh et b) que la réponse du POST puisse être mise en cache (par section 9.5), de sorte que par exemple un POST ultérieur peut recevoir la même réponse?
Diomidis Spinellis
3
Ceci est clarifié par HTTPbis; voir mnot.net/blog/2012/09/24/caching_POST pour un résumé.
Mark Nottingham
68

Selon RFC 2616 Section 9.5:

"Les réponses à la méthode POST ne peuvent pas être mises en cache, À MOINS QUE la réponse n'inclue les champs d'en-tête Cache-Control ou Expires appropriés."

Donc, OUI, vous pouvez mettre en cache la réponse à la requête POST, mais uniquement si elle arrive avec les en-têtes appropriés. Dans la plupart des cas, vous ne souhaitez pas mettre en cache la réponse. Mais dans certains cas - comme si vous n'enregistrez aucune donnée sur le serveur - c'est tout à fait approprié.

Notez cependant que de nombreux navigateurs, y compris Firefox 3.0.10 actuel, ne mettront pas en cache la réponse POST quels que soient les en-têtes. IE se comporte plus intelligemment à cet égard.

Maintenant, je veux clarifier une certaine confusion ici concernant RFC 2616 S. 13.10. La méthode POST sur un URI "n'invalide pas la ressource pour la mise en cache" comme certains l'ont indiqué ici. Il rend obsolète une version précédemment mise en cache de cet URI, même si ses en-têtes de contrôle de cache indiquaient une fraîcheur de plus longue durée.


la source
2
+1 redémarrage, merci d'avoir expliqué le problème des en-têtes et d'avoir corrigé les déclarations erronées concernant 13.10. Surprenant ces mauvaises réponses ont reçu tant de votes positifs.
Eugene Beresovsky
3
Quelle est la différence entre «invalider la ressource pour la mise en cache» et «rendre une version mise en cache de l'URI périmée»? Êtes-vous en train de dire que le serveur est autorisé à mettre en cache une réponse POST, mais pas les clients?
Gili
1
«rendre une version mise en cache de l'URI périmée» s'applique lorsque vous utilisez le même URI pour les requêtes GETet POST. Si vous êtes un cache assis entre le client et le serveur, vous voyez GET /fooet vous mettez en cache la réponse. Ensuite , vous voyez POST /fooalors vous devez invalider la réponse en cache de GET /foomême si la POSTréponse ne comprend pas les en- têtes de contrôle de cache , car ils sont les mêmes URI , donc le prochain GET /fooaura revalider même si les en- têtes d' origine indiqué le cache serait encore en direct (si vous n'aviez pas vu la POST /foodemande)
Stephen Connolly
But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.. Quel est donc l'intérêt d'une telle API POST en premier lieu?
Siddhartha
33

Global:

Fondamentalement, POST n'est pas une opération idempotente . Vous ne pouvez donc pas l'utiliser pour la mise en cache. GET doit être une opération idempotente, elle est donc couramment utilisée pour la mise en cache.

Veuillez consulter la section 9.1 du HTTP 1.1 RFC 2616 S. 9.1 .

Autre que la sémantique de la méthode GET:

La méthode POST elle-même est sémantiquement destinée à publier quelque chose sur une ressource. POST ne peut pas être mis en cache car si vous faites quelque chose une fois contre deux fois contre trois fois, vous modifiez la ressource du serveur à chaque fois. Chaque demande compte et doit être envoyée au serveur.

La méthode PUT elle-même est sémantiquement destinée à placer ou créer une ressource. C'est une opération idempotente, mais elle ne sera pas utilisée pour la mise en cache car un DELETE aurait pu se produire entre-temps.

La méthode DELETE elle-même est sémantiquement destinée à supprimer une ressource. C'est une opération idempotente, mais elle ne sera pas utilisée pour la mise en cache car un PUT aurait pu se produire entre-temps.

Concernant la mise en cache côté client:

Un navigateur Web transmettra toujours votre demande même s'il a une réponse d'une opération POST précédente. Par exemple, vous pouvez envoyer des e-mails avec Gmail à quelques jours d'intervalle. Ils peuvent avoir le même sujet et le même corps, mais les deux e-mails doivent être envoyés.

Concernant la mise en cache proxy:

Un serveur HTTP proxy qui transmet votre message au serveur ne mettra jamais en cache autre chose qu'une requête GET ou HEAD.

Concernant la mise en cache du serveur:

Un serveur par défaut ne traiterait pas automatiquement une requête POST en vérifiant son cache. Mais bien sûr, une requête POST peut être envoyée à votre application ou à votre complément et vous pouvez avoir votre propre cache que vous lisez lorsque les paramètres sont les mêmes.

Invalidation d'une ressource:

La vérification de HTTP 1.1 RFC 2616 S. 13.10 montre que la méthode POST doit invalider la ressource pour la mise en cache.

Brian R. Bondy
la source
9
"Fondamentalement, POST n'est pas une opération idempotente. Vous ne pouvez donc pas l'utiliser pour la mise en cache." C'est tout simplement faux, et cela n'a pas vraiment de sens, voir la réponse de reBoot pour plus de détails. Malheureusement, je ne peux pas encore voter contre, sinon je l'aurais fait.
Eugene Beresovsky
1
Eugene: J'ai changé "n'est pas" en "ne peut pas".
Brian R. Bondy
1
Merci Brian, ça sonne mieux. Mon problème avec votre "POST pas idemp. -> ne peut pas être mis en cache" était cependant - et je ne l'ai pas assez clairement expliqué - même si une opération n'est pas idempotente, ce qui ne signifie pas qu'elle ne peut pas être mise en cache. Je suppose que la question est de savoir si vous le regardez du point de vue du serveur, qui offre les données et connaît sa sémantique, ou le regardez-vous du côté de la réception (que ce soit un proxy de mise en cache, etc. ou un client) . Si c'est le pov client / proxy, je suis totalement d'accord avec votre message. Si c'est le serveur pov, si le serveur dit: "le client peut mettre en cache", alors le client peut mettre en cache.
Eugene Beresovsky
1
Eugene: Si cela fait une différence qu'il soit appelé une ou 5 fois, par exemple si vous publiez un message dans une liste, alors vous voulez que cet appel atteigne le serveur 5 fois, n'est-ce pas? Et vous ne voulez pas le mettre en cache pour qu'il n'atteigne pas le serveur, n'est-ce pas? Parce qu'il y a des effets secondaires importants.
Brian R. Bondy
[suite] Je n'ai cependant pas décidé si le serveur doit effectivement envoyer l'en-tête d'expiration de cache autorisant UNIQUEMENT si l'opération est idempotente. Cela a du sens, cependant, je suppose. [vient de voir votre réponse]: d'accord, donc je pense que j'ai pris ma décision: le serveur ne devrait signaler la possibilité de mise en cache qu'en cas d'idempotence - et cela pourrait également être un POST, en particulier compte tenu de la nécessité de X-HTTP-Method-Override dans certains cas.
Eugene Beresovsky
6

Si vous mettez en cache une réponse POST, elle doit être dirigée vers l'application Web. C'est ce que signifie «Les réponses à cette méthode ne peuvent pas être mises en cache, à moins que la réponse n'inclue les champs d'en-tête Cache-Control ou Expires appropriés».

On peut supposer en toute sécurité que l'application, qui sait si les résultats d'un POST sont idempotents ou non, décide d'attacher ou non les en-têtes de contrôle de cache nécessaires et appropriés. Si des en-têtes suggérant que la mise en cache est autorisée sont présents, l'application vous indique que le POST est, en réalité, un super-GET; que l'utilisation de POST n'était requise qu'en raison de la quantité de données inutiles et non pertinentes (à l'utilisation de l'URI comme clé de cache) nécessaires pour effectuer l'opération idempotente.

Les GET suivants peuvent être servis à partir du cache sous cette hypothèse.

Une application qui ne parvient pas à joindre les en-têtes nécessaires et corrects pour différencier les réponses POST pouvant être mises en cache et non cachable est responsable de tout résultat de mise en cache non valide.

Cela dit, chaque POST qui atteint le cache nécessite une validation à l'aide d'en-têtes conditionnels. Cela est nécessaire pour actualiser le contenu du cache afin d'éviter que les résultats d'un POST ne soient reflétés dans les réponses aux demandes avant l'expiration de la durée de vie de l'objet.

JohnS
la source
4

Mark Nottingham a analysé quand il est possible de mettre en cache la réponse d'un POST. Notez que les requêtes suivantes qui souhaitent tirer parti de la mise en cache doivent être des requêtes GET ou HEAD. Voir aussi la sémantique http

Les POST ne traitent pas les représentations d'état identifié, 99 fois sur 100. Cependant, il y a un cas où c'est le cas; lorsque le serveur fait tout son possible pour dire que cette réponse POST est une représentation de son URI, en définissant un en-tête Content-Location qui est le même que l'URI de la requête. Lorsque cela se produit, la réponse POST est comme une réponse GET au même URI; il peut être mis en cache et réutilisé - mais uniquement pour les futures requêtes GET.

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

dschulten
la source
4

Si vous vous demandez si vous pouvez mettre en cache une demande de publication et essayez de rechercher une réponse à cette question, vous ne réussirez probablement pas. Lorsque vous recherchez "demande de publication de cache", le premier résultat est cette question StackOverflow.

Les réponses sont un mélange confus de comment la mise en cache devrait fonctionner, comment la mise en cache fonctionne selon la RFC, comment la mise en cache devrait fonctionner selon la RFC et comment la mise en cache fonctionne dans la pratique. Commençons par la RFC, parcourons une démonstration du fonctionnement réel du navigateur, puis parlons des CDN, de GraphQL et d'autres domaines de préoccupation.

RFC 2616

Selon la RFC, les requêtes POST doivent invalider le cache:

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

Ce langage suggère que les requêtes POST ne peuvent pas être mises en cache, mais ce n'est pas vrai (dans ce cas). Le cache n'est invalidé que pour les données précédemment stockées. La RFC (semble) clarifier explicitement que oui, vous pouvez mettre en cache les POSTdemandes:

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.

Malgré cette langue, la définition de Cache-Controlne doit pas mettre en cache les POSTrequêtes suivantes sur la même ressource. POSTles demandes doivent être envoyées au serveur:

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.

Comment cela peut-il avoir un sens? Eh bien, vous ne mettez pas en cache la POSTdemande, vous mettez en cache la ressource.

Le corps de la réponse POST ne peut être mis en cache que pour les requêtes GET ultérieures adressées à la même ressource. Définissez l'en Location- Content-Locationtête ou dans la réponse POST pour indiquer quelle ressource le corps représente. Ainsi, le seul moyen techniquement valide de mettre en cache une requête POST, est pour les GET suivants sur la même ressource.

La bonne réponse est à la fois:

  • "Oui, le RFC vous permet de mettre en cache les requêtes POST pour les GET suivants sur la même ressource"
  • "non, la RFC ne vous permet pas de mettre en cache les requêtes POST pour les POST suivants car POST n'est pas idempotent et doit être écrit sur le serveur"

Bien que la RFC autorise la mise en cache des demandes vers la même ressource, en pratique, les navigateurs et les CDN n'implémentent pas ce comportement et ne vous permettent pas de mettre en cache les demandes POST.

Sources:

Démonstration du comportement du navigateur

Compte tenu de l'exemple d'application JavaScript suivant (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!')
    })

Et étant donné l'exemple de page Web suivant (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>

Installez NodeJS, Express et démarrez l'application JavaScript. Ouvrez la page Web dans votre navigateur. Essayez différents scénarios pour tester le comportement du navigateur:

  • Cliquer sur "Déclencher la requête GET" affiche le même "décompte" à chaque fois (la mise en cache HTTP fonctionne).
  • Cliquer sur "Déclencher la requête POST" déclenche un décompte différent à chaque fois (la mise en cache HTTP pour POST ne fonctionne pas).
  • Cliquer sur «Déclencher la requête GET», «Déclencher la requête POST» et «Déclencher la requête GET» montre que la requête POST invalide le cache de la requête GET.
  • Cliquer sur «Déclencher la requête POST» puis «Déclencher la requête GET» montre que les navigateurs ne mettront pas en cache les requêtes POST pour les requêtes GET suivantes même si cela est autorisé par la RFC.

Cela montre que, même si vous pouvez définir les en Cache-Control- Content-Locationtêtes et de réponse, il n'y a aucun moyen de faire en sorte qu'un navigateur cache une requête HTTP POST.

Dois-je suivre la RFC?

Le comportement du navigateur n'est pas configurable, mais si vous n'êtes pas un navigateur, vous n'êtes pas nécessairement lié par les règles de la RFC.

Si vous écrivez du code d'application, rien ne vous empêche de mettre explicitement en cache les requêtes POST (pseudocode):

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
}

Les CDN, les proxys et les passerelles ne doivent pas nécessairement suivre la RFC non plus. Par exemple, si vous utilisez Fastly comme CDN, Fastly vous permet d'écrire une logique VCL personnalisée pour mettre en cache les requêtes POST .

Dois-je mettre en cache les requêtes POST?

La mise en cache ou non de votre requête POST dépend du contexte.

Par exemple, vous pouvez interroger Elasticsearch ou GraphQL à l'aide de POST où votre requête sous-jacente est idempotente. Dans ces cas, il peut être judicieux ou non de mettre en cache la réponse en fonction du cas d'utilisation.

Dans une API RESTful, les requêtes POST créent généralement une ressource et ne doivent pas être mises en cache. C'est aussi la compréhension de la RFC du POST selon laquelle ce n'est pas une opération idempotente.

GraphQL

Si vous utilisez GraphQL et que vous avez besoin de la mise en cache HTTP sur les CDN et les navigateurs, déterminez si l'envoi de requêtes à l'aide de la méthode GET répond à vos exigences au lieu de POST . En guise de mise en garde, différents navigateurs et CDN peuvent avoir des limites de longueur d'URI différentes, mais la safelisting des opérations (liste blanche de requêtes), en tant que meilleure pratique pour les applications GraphQL de production externes, peut raccourcir les URI.

timrs2998
la source
3

Si c'est quelque chose qui ne change pas réellement les données de votre site, il doit s'agir d'une requête GET. Même s'il s'agit d'un formulaire, vous pouvez toujours le définir comme une demande d'obtention. Bien que, comme d'autres le soulignent, vous puissiez mettre en cache les résultats d'un POST, cela n'aurait pas de sens sémantique car un POST par définition modifie des données.

Kibbee
la source
La requête POST peut ne pas modifier les données utilisées pour générer la page de réponse, auquel cas il peut être judicieux de mettre en cache la réponse.
David Z
David Z: Si un POST modifie des données, la réponse doit certainement donner une indication de succès / échec. Pas nécessaire exactement, mais je ne peux pas penser à une situation où un POST changerait les données et la réponse serait statique.
Morvael
6
Si les données de paramètre sont trop longues, une requête GET ne fonctionnera pas avec tous les serveurs, donc POST est nécessaire, surtout si la source doit s'exécuter sur des serveurs que l'auteur du code ne configure pas.
Gogowitsch
@Gogowitsch très vrai, vous rencontrerez un code d'erreur 414 - stackoverflow.com/a/2891598/792238
Siddhartha
-2

Avec Firefox 27.0 et avec httpfox, le 19 mai 2014, j'ai vu une ligne de ceci: 00: 03: 58.777 0.488 657 (393) POST (Cache) text / html https://users.jackiszhp.info/S4UP

Clairement, la réponse d'une méthode de publication est mise en cache, et elle est également en https. Incroyable!

user1462586
la source
-3

POST est utilisé dans Ajax avec état. Le renvoi d'une réponse en cache pour un POST annule le canal de communication et les effets secondaires de la réception d'un message. C'est très, très mauvais. C'est aussi une vraie douleur à retrouver. Fortement recommandé contre.

Un exemple trivial serait un message qui, comme effet secondaire, paie votre salaire 10 000 $ la semaine en cours. Vous ne voulez PAS obtenir le "OK, c'est passé!" page précédente qui a été mise en cache la semaine dernière. D'autres cas concrets plus complexes entraînent une hilarité similaire.

Seigneur Dragon
la source
3
Pas vraiment une réponse - POST utilisé pour toutes sortes de choses et il y a parfois des raisons valables de vouloir mettre en cache la réponse.
Alexei Levenkov