Le navigateur, lui, a de la mémoire
Souviens-toi de la leçon précédente : le serveur n'a pas de mémoire. Chaque requête arrive vierge, il a oublié la précédente, et c'est le cookie qui rattrape le coup. Mais il y a une mémoire dont on n'a pas encore parlé, et elle est de ton côté : ton navigateur, lui, retient des choses. Et ici, ce n'est pas pour te reconnaître. C'est une arme de vitesse.
Reprends le fil rouge de la leçon 4 : chaque aller-retour coûte. Une requête part, traverse le réseau, le serveur répond, ça revient. Du temps, à chaque fois. Le cache, c'est l'outil qui supprime des allers-retours entiers. Le meilleur aller-retour, c'est celui qu'on ne fait pas.
Pose-toi la question : tu reviens sur un site que tu connais. Le logo, le fichier CSS, les polices... tout ça n'a pas changé depuis hier. Pourquoi ton navigateur irait-il les re-télécharger ? Ce serait du pur gâchis. Cette leçon explique comment il évite ce gâchis, et comment toi, développeur, tu le pilotes.
Trois niveaux de réponse, du plus rapide au plus lent
Quand ton navigateur a besoin d'un fichier qu'il a déjà vu, il a trois façons de répondre. Du plus rapide au plus lent :
- Cache frais : zéro requête. Le navigateur a une copie, et il sait qu'elle est encore valable. Il la ressert directement depuis son disque. Aucun aller-retour. C'est instantané.
- Revalidation : un aller-retour minuscule. Le navigateur a une copie, mais il n'est plus sûr qu'elle soit à jour. Il demande au serveur « ma version est-elle toujours bonne ? ». Si oui, le serveur répond
304, sans renvoyer le fichier. Un aller-retour, mais quasi vide. - Cache raté : tout re-télécharger. Pas de copie utilisable. Le navigateur refait une vraie requête, le serveur renvoie le fichier complet en
200. L'aller-retour entier, body compris.
Tout l'art du cache, c'est de rester le plus possible dans le premier cas, de tomber dans le deuxième quand on n'est pas sûr, et d'éviter le troisième quand rien n'a changé. La suite explique les en-têtes qui pilotent ça.
Cache-Control, raconté simplement
C'est le serveur qui décide. Quand il envoie un fichier, il ajoute un en-tête Cache-Control qui dit au navigateur comment le garder. Quelques directives suffisent à tout comprendre :
max-age=3600: « garde cette copie 3600 secondes (1 heure) sans rien me redemander ». Pendant cette heure, c'est le cas le plus rapide : zéro requête. La copie est dite fraîche.no-cache: « garde la copie, mais revalide à chaque fois avant de la servir ». Le navigateur garde bien le fichier, mais il redemande systématiquement au serveur « toujours bon ? ». C'est le cas de l'aller-retour minuscule.no-store: « ne garde rien du tout ». Le vrai « pas de cache ». Le navigateur ne stocke même pas une copie, il re-télécharge à chaque fois. On le réserve aux données sensibles (une page de compte bancaire).public/private:publicautorise les caches partagés (comme un CDN) à stocker la réponse ;privatela réserve au navigateur de l'utilisateur, parce qu'elle lui est personnelle.
Le piège du nom : no-cache ne veut PAS dire « pas de cache ». C'est le contraire de ce que le nom laisse croire. no-cache autorise le navigateur à garder la copie, il l'oblige juste à la revalider avant chaque usage. Le vrai « ne stocke rien » s'appelle no-store. Confondre les deux est l'erreur la plus courante sur ce sujet : on croit avoir désactivé le cache avec no-cache alors qu'on a juste forcé une revalidation.
La revalidation : ETag et le 304
Regardons le cas du milieu de plus près, parce que c'est le plus malin. Comment le navigateur demande-t-il « ma version est-elle toujours bonne ? » sans re-télécharger le fichier ? Avec une empreinte.
Le mécanisme complet, étape par étape :
- La première réponse porte un
ETag. Quand le serveur envoie le fichier, il ajoute un en-têteETag: "v42". C'est l'empreinte de cette version précise du fichier. Le navigateur range le fichier ET son empreinte. - Plus tard, le navigateur redemande avec
If-None-Match. Au lieu d'une requête nue, il envoieIf-None-Match: "v42": « j'ai déjà la version v42, est-elle toujours d'actualité ? ». - Si rien n'a changé, le serveur répond
304 Not Modified. Rappelle-toi la leçon 5 : le304fait partie de la famille des3xx. Il signifie « ta copie est toujours bonne, ressers-la ». Surtout, cette réponse n'a aucun body : le serveur ne renvoie pas le fichier, juste le feu vert. - Le navigateur ressert sa copie locale. Un aller-retour, oui, mais quasi zéro octet transféré. Au lieu de re-télécharger 80 Ko de logo, on a échangé quelques octets d'en-têtes.
C'est ça, la beauté de la revalidation : on paie le coût minimal (un aller-retour vide) pour avoir la certitude d'être à jour, au lieu de payer le coût maximal (re-télécharger tout) par précaution.
Tu charges une page : son logo, son CSS et ses polices arrivent tous en 200. Tu appuies sur F5 pour recharger la page. Que vont devenir ces 200 dans la colonne « statut » de l'onglet réseau ?
Voir la réponse
Ils changent presque tous de visage. Les fichiers encore frais (ceux avec un max-age non expiré) affichent 200 (from disk cache) : zéro requête, servis depuis le disque. Ceux dont la copie doit être revalidée affichent 304 Not Modified : un aller-retour minuscule, sans body. Et si un fichier a vraiment changé ou n'était pas cachable, lui seul repart en vrai 200 complet. Le tableau qui suit te montre les deux colonnes côte à côte.
L'onglet réseau, premier chargement contre rechargement
Ouvre les deux volets et compare. Le même site, deux visites. La colonne « taille » raconte toute l'histoire du cache.
📥 Premier chargement (tout est neuf)
| Fichier | Statut | Taille |
|---|---|---|
index.html | 200 | 14 Ko |
style.css | 200 | 42 Ko |
logo.svg | 200 | 8 Ko |
inter.woff2 | 200 | 96 Ko |
Total transféré : 160 Ko. Tout part en 200, body complet, rien n'est encore en cache.
🔄 Rechargement (le cache entre en jeu)
| Fichier | Statut | Taille |
|---|---|---|
index.html | 304 | 0,3 Ko |
style.css | 200 (cache disque) | 0 Ko |
logo.svg | 200 (cache disque) | 0 Ko |
inter.woff2 | 200 (cache disque) | 0 Ko |
Total transféré : 0,3 Ko au lieu de 160 Ko. Le HTML a été revalidé (304, juste les en-têtes), les assets frais sont servis depuis le disque (0 Ko). La page s'affiche presque instantanément.
Les trois scénarios en un coup d'oeil
Garde ce schéma en tête. Trois lignes, trois coûts : le cache frais ne touche jamais le réseau, la revalidation y va mais en revient presque vide, le cache raté paie le plein tarif.
Et le HTML, on le cache ?
Tu remarques dans le tableau que le HTML, lui, repart en 304 à chaque fois, alors que le CSS et les polices restent muets sur le disque. Ce n'est pas un hasard. Le HTML change souvent (un nouveau titre, un prix mis à jour), donc on le cache peu, ou on le force à revalider. Les assets, eux, sont stables et on les garde longtemps.
Mais alors, comment changer un CSS qui est caché pour un an ? On change son nom. Au lieu de style.css, on sert style.css?v=12, ou mieux, un nom haché comme style.a1b2c3.css. Une nouvelle version a un nouveau nom, donc une nouvelle URL, donc le navigateur la voit comme un fichier neuf qu'il télécharge. C'est exactement pour ça que les outils de build (Vite, webpack) renomment automatiquement tes fichiers à chaque build : ils te donnent un cache long et agressif sans jamais te coincer avec une vieille version.
The browser, though, has a memory
Remember the previous lesson: the server has no memory. Every request arrives blank, it forgot the last one, and the cookie is what saves the day. But there's a memory we haven't talked about yet, and it's on your side: your browser does remember things. And here, it's not to recognise you. It's a speed weapon.
Pick up the through-line from lesson 4: every round trip costs. A request goes out, crosses the network, the server answers, it comes back. Time, every single time. Caching is the tool that removes whole round trips. The best round trip is the one you never make.
Ask yourself: you come back to a site you know. The logo, the CSS file, the fonts... none of it has changed since yesterday. Why would your browser re-download them? That would be pure waste. This lesson explains how it avoids that waste, and how you, the developer, steer it.
Three levels of answer, from fastest to slowest
When your browser needs a file it has already seen, it has three ways to answer. From fastest to slowest:
- Fresh cache: zero request. The browser has a copy, and it knows the copy is still valid. It serves it straight from its disk. No round trip. Instant.
- Revalidation: a tiny round trip. The browser has a copy, but it's no longer sure it's up to date. It asks the server "is my version still good?". If yes, the server replies
304, without sending the file back. One round trip, but nearly empty. - Cache miss: re-download everything. No usable copy. The browser makes a real request, the server returns the full file with a
200. The whole round trip, body included.
The whole art of caching is to stay in the first case as much as possible, fall into the second when you're not sure, and avoid the third when nothing has changed. What follows explains the headers that steer this.
Cache-Control, told simply
The server decides. When it sends a file, it adds a Cache-Control header that tells the browser how to keep it. A handful of directives explain it all:
max-age=3600— "keep this copy for 3600 seconds (1 hour) without asking me again". During that hour, it's the fastest case: zero request. The copy is said to be fresh.no-cache— "keep the copy, but revalidate every time before serving it". The browser does keep the file, it just systematically re-asks the server "still good?". This is the tiny round-trip case.no-store— "don't keep anything at all". The real "no cache". The browser doesn't even store a copy, it re-downloads every time. Reserve it for sensitive data (a bank account page).public/private—publiclets shared caches (like a CDN) store the response;privatereserves it for the user's browser, because it's personal to them.
The naming trap: no-cache does NOT mean "no cache". It's the opposite of what the name suggests. no-cache lets the browser keep the copy, it just forces it to revalidate before every use. The real "store nothing" is called no-store. Confusing the two is the most common mistake on this topic: you think you disabled caching with no-cache when you just forced a revalidation.
Revalidation: ETag and the 304
Let's look at the middle case more closely, because it's the cleverest. How does the browser ask "is my version still good?" without re-downloading the file? With a fingerprint.
The full mechanism, step by step:
- The first response carries an
ETag. When the server sends the file, it adds anETag: "v42"header. It's the fingerprint of that precise version of the file. The browser stores the file AND its fingerprint. - Later, the browser re-asks with
If-None-Match. Instead of a bare request, it sendsIf-None-Match: "v42": "I already have version v42, is it still current?". - If nothing changed, the server replies
304 Not Modified. Remember lesson 5: the304belongs to the3xxfamily. It means "your copy is still good, re-serve it". Crucially, this response has no body: the server doesn't send the file back, just the green light. - The browser re-serves its local copy. One round trip, yes, but nearly zero bytes transferred. Instead of re-downloading 80 KB of logo, you exchanged a few bytes of headers.
That's the beauty of revalidation: you pay the minimal cost (an empty round trip) to be certain you're up to date, instead of paying the maximal cost (re-download everything) just in case.
You load a page: its logo, CSS and fonts all arrive with a 200. You press F5 to reload. What will those 200s become in the "status" column of the network tab?
Show the answer
Almost all of them change face. The still-fresh files (those with a non-expired max-age) show 200 (from disk cache): zero request, served from disk. Those whose copy must be revalidated show 304 Not Modified: a tiny round trip, no body. And if a file truly changed or wasn't cacheable, that one alone goes back out as a real full 200. The table below shows both columns side by side.
The network tab: first load versus reload
Open both panels and compare. Same site, two visits. The "size" column tells the whole caching story.
📥 First load (everything is new)
| File | Status | Size |
|---|---|---|
index.html | 200 | 14 KB |
style.css | 200 | 42 KB |
logo.svg | 200 | 8 KB |
inter.woff2 | 200 | 96 KB |
Total transferred: 160 KB. Everything goes out as 200, full body, nothing is cached yet.
🔄 Reload (caching kicks in)
| File | Status | Size |
|---|---|---|
index.html | 304 | 0.3 KB |
style.css | 200 (disk cache) | 0 KB |
logo.svg | 200 (disk cache) | 0 KB |
inter.woff2 | 200 (disk cache) | 0 KB |
Total transferred: 0.3 KB instead of 160 KB. The HTML was revalidated (304, just the headers), the fresh assets are served from disk (0 KB). The page renders almost instantly.
The three scenarios at a glance
Keep this diagram in mind. Three lines, three costs: fresh cache never touches the network, revalidation goes out but comes back nearly empty, the cache miss pays full price.
And the HTML itself, do we cache it?
You'll notice in the table that the HTML goes back out as a 304 every time, while the CSS and fonts stay silent on disk. That's no accident. HTML changes often (a new title, an updated price), so we cache it little, or force it to revalidate. The assets, on the other hand, are stable and we keep them a long time.
But then how do you change a CSS file that's cached for a year? You change its name. Instead of style.css, you serve style.css?v=12, or better, a hashed name like style.a1b2c3.css. A new version has a new name, so a new URL, so the browser sees it as a brand-new file to download. That's exactly why build tools (Vite, webpack) automatically rename your files on every build: they give you a long, aggressive cache without ever trapping you with an old version.
🎯 Pratique
S'entraîner (clique pour ouvrir) :
💬 Ré-explique sans regarder
Explique à un collègue le voyage d'un ETag : comment le serveur, plus tard le navigateur, puis un 304 évitent de re-télécharger un fichier inchangé.
ETag: "v42", l'empreinte de cette version. Le navigateur garde le fichier et l'empreinte. Plus tard, il redemande avec If-None-Match: "v42". Si rien n'a changé, le serveur répond 304 Not Modified, sans body, et le navigateur ressert sa copie locale. Bilan : un aller-retour, quasi zéro octet, au lieu de re-télécharger tout le fichier.🧠 Rappel libre
Sans remonter : quelle est la différence réelle entre no-cache et no-store ?
no-cache n'interdit PAS le cache : le navigateur garde la copie, mais il doit la revalider (ETag / 304) avant chaque usage. no-store est le vrai « pas de cache » : le navigateur ne stocke rien et re-télécharge à chaque fois. On réserve no-store aux données sensibles.⚖️ Juge le code de l'IA
Un client te dit que ses utilisateurs voient parfois une vieille version d'une page. Tu demandes à l'IA de régler ça. Elle répond : « Réglé ! J'ai mis Cache-Control: no-cache sur toutes les réponses pour désactiver complètement le cache. » Tu acceptes, ou tu rejettes ?
no-cache ne désactive PAS le cache, il force une revalidation à chaque fois (le navigateur garde la copie, le vrai « ne stocke rien » est no-store). Ensuite, désactiver le cache est rarement le bon fix : on perd toute la vitesse pour rien. Le vrai problème est ailleurs : les assets (CSS, JS) doivent garder un cache long ET être versionnés par leur nom (style.a1b2c3.css), pour qu'une nouvelle version ait une nouvelle URL. C'est ça qui sert toujours la dernière version sans sacrifier la performance.Cache-Control: max-age=3600. Tu recharges la page 5 minutes plus tard. Que fait le navigateur pour ce fichier ?Cache-Control: no-cache en pensant « ne mets jamais en cache ». A-t-il raison, et sinon que fait vraiment no-cache ?If-None-Match: "v42" et reçoit un 304 Not Modified. Qu'a transféré le serveur, et que fait le navigateur ?style.css, tu déploies, mais un client voit toujours l'ancien design. Quelles explications par le cache sont plausibles ?You now know how to save whole round trips with caching. Lesson 8, the last leg of the journey: CORS and HTTP/2, HTTP/3. Why your fetch to another API gets refused by the browser, and what HTTP/2 then HTTP/3 changed under the hood to make everything else faster.
Lesson 8: CORS and HTTP/2, HTTP/3 →