Last-Modified est un en-tête de réponse HTTP qui indique la date et l’heure de la dernière modification d’une ressource sur le serveur, permettant au client d’effectuer des requêtes conditionnelles via If-Modified-Since. Si la ressource n’a pas changé depuis la date spécifiée, le serveur retourne 304 Not Modified sans envoyer le corps de la réponse, ce qui économise considérablement la bande passante. Selon RFC 7232 (IETF, 2014), les requêtes conditionnelles avec Last-Modified réduisent le temps de chargement des pages de 30 à 60% lors des visites répétées. L’en-tête est automatiquement pris en charge par la plupart des serveurs HTTP et des proxies.
Points clés
Last-Modified est un en-tête HTTP appartenant au groupe des en-têtes de requêtes conditionnelles. Le serveur l’ajoute à une réponse GET ou HEAD, indiquant la date et l’heure de la dernière modification de la ressource demandée au format HTTP-date : Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. Le client (navigateur, application mobile, proxy) enregistre cette date avec la ressource mise en cache. Lors d’une requête répétée, le client envoie l’en-tête If-Modified-Since avec la même date, et le serveur la compare avec l’heure actuelle de modification de la ressource.
Le protocole de requêtes conditionnelles avec Last-Modified est défini dans la RFC 7232 et est pris en charge par tous les serveurs HTTP modernes. Le format de la date est strictement réglementé — uniquement GMT (Greenwich Mean Time) sans indication de fuseau horaire. Le serveur doit retourner la date dans trois formats possibles : RFC 1123 (standard), RFC 850 (obsolète) ou ANSI C asctime. Dans la pratique, presque tous les serveurs utilisent le format RFC 1123 avec une longueur fixe de 29 caractères.
Last-Modified appartient à la catégorie des mécanismes de validation de cache : il ne dit pas au client si la réponse peut être mise en cache, mais fournit un outil pour vérifier l’actualité d’une ressource déjà en cache. La politique de cache est définie séparément via l’en-tête Cache-Control. Selon une étude d’Akamai (2025), une configuration correcte de Last-Modified avec Cache-Control réduit la charge sur les serveurs d’origine jusqu’à 70% pour le contenu statique.
L’en-tête Last-Modified a été défini dès HTTP/1.0 (RFC 1945, 1996) et est devenu l’un des premiers mécanismes de gestion de cache sur le Web. Avant l’apparition d’ETag dans HTTP/1.1, c’était le seul moyen d’effectuer des requêtes conditionnelles. Malgré son âge, l’en-tête reste pertinent grâce à sa simplicité — le serveur n’a pas besoin de calculer un hash du contenu, il lui suffit de lire l’horodatage du fichier dans le système de fichiers ou le champ updated_at de la base de données.
Le cycle complet comprend trois étapes. Lors de la première requête, le serveur retourne la ressource avec l’en-tête Last-Modified et le statut HTTP 200 OK. Le client met en cache la réponse avec la date. Lors d’une requête répétée, le client envoie l’en-tête If-Modified-Since avec la date enregistrée. Le server compare cette date avec l’heure actuelle de modification de la ressource. Si la ressource n’a pas changé — il retourne 304 Not Modified avec un corps vide. Si elle a changé — 200 OK avec de nouvelles données et un nouveau Last-Modified.
// Première requête — le serveur retourne la ressource avec une date
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT
[{"id": 1, "name": "Alice"}]
// Requête répétée — le client envoie la date enregistrée
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT
// Réponse — les données n’ont pas changé
HTTP/1.1 304 Not Modified
Pour les applications mobiles, Last-Modified est particulièrement utile pour la synchronisation des données. L’application enregistre la date de la dernière mise à jour réussie et l’envoie au serveur dans If-Modified-Since. S’il y a plus de données ou si elles ont changé — le serveur retourne l’ensemble complet. Sinon — 304, et l’application utilise la copie locale. OkHttp et URLSession prennent en charge ce mécanisme automatiquement via leurs systèmes de cache intégrés.
Pour les fichiers statiques, Nginx et Apache obtiennent la date des attributs du système de fichiers — mtime (heure de modification). Pour le contenu dynamique, le code du serveur doit définir explicitement Last-Modified en fonction de la logique métier : le champ updated_at de la base de données, la date du dernier commit Git, l’horodatage de l’artefact de build. Si Last-Modified n’est pas défini explicitement, le serveur peut ne pas envoyer l’en-tête du tout, et le client ne pourra pas effectuer de requêtes conditionnelles par date.
Last-Modified et ETag remplissent une tâche similaire — permettre au client de vérifier l’actualité du cache — mais ont des différences fondamentales. Last-Modified utilise un horodatage, ETag utilise un identifiant de version unique. Chaque approche a ses scénarios où elle est plus efficace, et la spécification HTTP recommande d’utiliser les deux en-têtes ensemble.
| Critère | Last-Modified | ETag |
|---|---|---|
| Essence | Date de dernière modification | Identifiant de version unique |
| Précision | À la seconde près | Au bit près (hash) |
| Complexité d’implémentation | Faible — automatique depuis le système de fichiers | Moyenne — nécessite un calcul de hash |
| Serveurs en cluster | Problème : mtime peut différer entre nœuds | Stable avec des données identiques entre nœuds |
| Support des plages | N’affecte pas les requêtes Range | Nécessite un ETag fort pour les plages |
| Recommandation | Pour les fichiers statiques et les API simples | Pour les API où la précision est importante |
Le principal avantage de Last-Modified est sa simplicité. Le serveur n’a pas besoin de calculer un hash du contenu, ce qui économise des ressources CPU pour chaque requête. Pour les projets à fort trafic servant des fichiers statiques ou des données avec des horodatages clairs, Last-Modified reste le choix optimal. ETag, en revanche, offre une précision absolue — changer une seule lettre dans une réponse JSON modifiera l’ETag, mais peut ne pas changer la date (si le fichier a été écrasé avec la même version).
La spécification recommande de retourner les deux en-têtes simultanément. Le serveur inclut Last-Modified et ETag dans la réponse 200 OK. Le client envoie les deux en-têtes conditionnels — If-Modified-Since et If-None-Match. Le serveur vérifie d’abord ETag (il a la priorité), puis Last-Modified. Si au moins l’un signale un changement — la réponse complète est retournée. Cela offre une flexibilité maximale : ETag garantit la précision, Last-Modified fournit une vérification de secours pour les clients qui ne supportent pas ETag.
La configuration de Last-Modified dépend du type de serveur. Pour Nginx et Apache, Last-Modified est défini automatiquement pour les fichiers statiques sur la base de mtime. Pour les applications dynamiques, l’en-tête doit être défini dans le code du serveur. Voyons la configuration sur les plates-formes populaires.
// Express.js — définir Last-Modified
app.get("/api/users", async (req, res) => {
const updatedAt = await getLastUpdate()
const ifModifiedSince = req.get("If-Modified-Since")
// Vérification d’If-Modified-Since
if (ifModifiedSince && new Date(ifModifiedSince)
>= updatedAt) {
return res.status(304).end()
}
const users = await getUsers()
res.set("Last-Modified", updatedAt.toUTCString())
res.json(users)
})
Dans l’exemple Express.js, le serveur obtient la date de la dernière mise à jour des données depuis la base, vérifie If-Modified-Since du client et, si le cache est toujours frais, retourne 304. Si les données ont changé — il définit un nouveau Last-Modified et retourne la réponse complète. toUTCString() convertit la date au format HTTP requis. En production, il est conseillé de mettre en cache updatedAt dans Redis pour éviter une requête à la base de données à chaque appel.
Nginx définit automatiquement Last-Modified pour les fichiers statiques en fonction de l’heure de dernière modification du fichier. Ce comportement peut être désactivé ou modifié via la directive etag (désactivation d’ETag) ou via le module ngx_http_headers_module. Pour les requêtes proxy vers le backend, Last-Modified est transmis depuis la réponse upstream sans modification. Important : si le backend ne retourne pas Last-Modified, Nginx ne l’ajoutera pas automatiquement pour les réponses dynamiques.
Last-Modified a plusieurs limitations connues. La principale est la précision à la seconde. Si une ressource change deux fois en une seconde, le client peut manquer la nouvelle version. Dans la pratique, c’est un scénario rare, mais pour les mises à jour à haute fréquence (flux de cotations, chats), ETag est recommandé. La deuxième limitation est le problème de cluster : sur différents serveurs, un fichier peut avoir un mtime différent en raison de la copie ou du déploiement, rendant Last-Modified incohérent.
La troisième limitation — le traitement d’If-Modified-Since avec une précision à la seconde peut générer des requêtes inutiles lors d’interrogations fréquentes du serveur. Si le client envoie If-Modified-Since toutes les 500 ms, le serveur retourne 200 OK chaque fois parce que la date n’a pas changé, mais la ressource a déjà été mise à jour. La solution est d’utiliser une combinaison avec ETag : ETag détectera le changement en une seconde, tandis que Last-Modified reste comme solution de secours.
Le quatrième problème — Last-Modified ne fait pas la distinction entre différentes versions d’une même ressource avec la même date. Si un fichier est restauré à partir d’une sauvegarde et que son mtime correspond à l’original, le client ne remarquera pas que le contenu a changé. ETag résout ce problème : le hash du contenu changera nécessairement lors de toute modification des données, indépendamment de l’horodatage. Pour les données critiques, utilisez toujours les deux en-têtes.
Questions fréquentes
Seulement GMT (Greenwich Mean Time) au format RFC 1123 : jour de la semaine, jour, mois, année, heures:minutes:secondes. Exemple : Wed, 02 Jul 2025 14:30:00 GMT. Le fuseau horaire est toujours GMT, les autres formats ne sont pas autorisés.
Techniquement oui, mais cela viole la RFC 7232. Si le serveur retourne une date future, les clients ne mettront pas à jour la ressource jusqu’à ce que cette date arrive. Une telle configuration est considérée comme une erreur — la date doit être dans le passé ou le présent.
Non, les requêtes conditionnelles If-Modified-Since fonctionnent uniquement avec GET et HEAD. Les requêtes POST ne sont pas mises en cache et n’utilisent pas la validation par date. Pour les vérifications d’actualité en POST, utilisez ETag ou des mécanismes personnalisés.
Cache-Control définit la politique de cache (durée maximale de stockage, qui peut mettre en cache), tandis que Last-Modified est un mécanisme de validation pour le cache expiré. Après l’expiration du max-age, le client envoie If-Modified-Since pour vérifier l’actualité.
Vérifiez que le serveur définit bien l’en-tête à partir de la source correcte — base de données, système de fichiers ou API. Pour les réponses dynamiques, assurez-vous d’appeler explicitement res.setHeader(“Last-Modified”, ...) dans le code du gestionnaire.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi