304 Not Modified
Utilisation
Le code de statut 304 Not Modified est envoyé lorsque la requête est sûre, comme une requête GET ou HEAD, et que le serveur détermine que la ressource n’a pas changé depuis la version stockée dans le cache du client. Cela économise de la bande passante, car le serveur ne retransmet pas des données que le client possède déjà.
Remarque
304 Not Modified n’est pas une redirection, bien qu’il partage la classe de statut 3xx. Les redirections (301, 302, 307, 308) envoient le client vers une autre URL via l’en-tête Location. 304 indique au client de réutiliser sa copie en cache de la même ressource, et aucun en-tête Location n’est envoyé. Les DevTools des navigateurs regroupent parfois 304 avec les redirections, ce qui ajoute à la confusion.
Remarque
« 200 (from disk cache) » dans les DevTools signifie que le navigateur a servi la ressource depuis le cache sans contacter le serveur, parce que le max-age de Cache-Control n’a pas expiré. 304 signifie que le navigateur a contacté le serveur pour revalider et que le serveur a confirmé que la copie en cache est à jour. Un 200 servi depuis le cache n’implique aucune requête réseau, tandis qu’un 304 implique un aller-retour.
Le serveur génère un ou plusieurs des en-têtes suivants dans sa réponse :
Comme l’objectif de la réponse 304 Not Modified est de minimiser la bande passante, le serveur omet les en-têtes non inclus dans la requête d’origine, sauf s’ils facilitent le processus de mise à jour du cache.
Pour les requêtes GET conditionnelles, les directives pertinentes sont If-None-Match et If-Modified-Since. Elles s’appuient sur un validateur tel qu’un ETag pour déterminer si la ressource doit être retransmise.
Exemple
Le client demande une ressource, reçoit un ETag identifiant la version, puis revalide plus tard. Le serveur constate que la ressource est inchangée et renvoie 304 Not Modified. À la troisième requête, une nouvelle version est disponible et une réponse 200 complète est envoyée avec un ETag mis à jour.
Remarque
Chaque fois que le serveur met à jour une ressource, un nouvel ETag est généré. En alternative, le serveur renvoie une date Last-Modified et le client utilise If-Modified-Since à la place, ce qui évite au serveur d’avoir à conserver un historique des ETag.
Impact SEO
Lorsque Googlebot reçoit un 304, le crawler constate que le contenu n’a pas changé depuis le dernier passage. Le pipeline d’indexation recalcule les signaux de classement pour l’URL, mais ne récupère ni ne réindexe le contenu.
Le piège du 304 périmé
Un 304 confirme que la version en cache est toujours à jour. Si le serveur a précédemment servi une page défectueuse (corps vide, contenu d’erreur) avec un 200 et que le crawler a mis le résultat en cache, un 304 ultérieur indique au crawler que la version défectueuse persiste. Le crawler cesse de revérifier, puisque le « contenu » n’a pas changé. Corriger la page ne suffit pas. Le serveur doit renvoyer un nouveau 200 avec le corps corrigé (et un nouvel ETag) pour remplacer l’erreur mise en cache. Ce scénario est difficile à déboguer, car la cause racine (une erreur transitoire passée) n’est plus visible.
Requête initiale
GET /news.html HTTP/1.1
Host: example.com
Réponse initiale
HTTP/1.1 200 OK
ETag: "1234000"
Content-Type: text/html
Content-Length: 1250
<message body contains requested resource>
Deuxième requête
GET /news.html HTTP/1.1
Host: example.com
If-None-Match: "1234000"
Deuxième réponse
HTTP/1.1 304 Not Modified
ETag: "1234000"
Troisième requête
GET /news.html HTTP/1.1
Host: example.com
If-None-Match: "1234000"
Troisième réponse
HTTP/1.1 200 OK
ETag: "1234001"
Content-Type: text/html
Content-Length: 1600
<message body contains requested resource>
Références de code
.NET
HttpStatusCode.NotModified
Rust
http::StatusCode::NOT_MODIFIED
Rails
:not_modified
Go
http.StatusNotModified
Symfony
Response::HTTP_NOT_MODIFIED
Python3.5+
http.HTTPStatus.NOT_MODIFIED
Java
java.net.HttpURLConnection.HTTP_NOT_MODIFIED
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_NOT_MODIFIED
Angular
@angular/common/http/HttpStatusCode.NotModified