1108 votes

Comment forcer le navigateur à recharger les fichiers CSS et JavaScript mis en cache ?

J'ai remarqué que certains navigateurs (en particulier, Firefox et Opéra ) sont très zélés dans l'utilisation de copies en cache de .css y .js même entre deux sessions de navigation. Cela pose un problème lorsque vous mettez à jour un de ces fichiers, mais que le navigateur de l'utilisateur continue à utiliser la copie en cache.

Quel est le moyen le plus élégant de forcer le navigateur de l'utilisateur à recharger le fichier lorsqu'il a été modifié ?

Idéalement, la solution ne devrait pas obliger le navigateur à recharger le fichier à chaque visite de la page.


J'ai trouvé John Millikin y de da5id suggestion pour être utile. Il s'avère qu'il existe un terme pour cela : auto-version .

J'ai posté une nouvelle réponse ci-dessous qui est une combinaison de ma solution originale et de la suggestion de John.

Une autre idée qui a été suggérée par SCdF serait d'ajouter une fausse chaîne de requête au fichier. (Un code Python, permettant d'utiliser automatiquement l'horodatage comme chaîne de requête fictive, a été ajouté à la base de données). présenté par pi. .)

Cependant, la question de savoir si le navigateur mettrait en cache un fichier avec une chaîne d'interrogation fait l'objet d'un débat. (N'oubliez pas que nous voulons que le navigateur mette le fichier en cache et l'utilise lors de visites ultérieures. Nous voulons seulement qu'il aille chercher le fichier à nouveau lorsqu'il a été modifié).

0 votes

Je l'ai mis dans mon .htaccess, et je n'ai jamais eu de problèmes avec les fichiers en cache : ExpiresActive On ExpiresDefault "modification" .

2 votes

Je suis tout à fait d'accord pour dire que l'ajout d'informations sur les versions à l'URL du fichier est de loin la meilleure solution. Cela fonctionne, tout le temps, pour tout le monde. Mais, si vous ne l'utilisez pas, et que vous avez juste besoin de recharger ce fichier CSS ou JS de temps en temps dans votre propre navigateur... ouvrez-le dans son propre onglet et appuyez sur SHIFT-reload (ou CTRL-F5) ! Vous pouvez faire la même chose en utilisant JS en chargeant un fichier dans une iframe (cachée), en attendant qu'il se charge, puis en appelant iframe.contentWindow.location.reload(true) . Voir la méthode (4) de stackoverflow.com/a/22429796/999120 - il s'agit d'images, mais la même chose s'applique.

3 votes

J'apprécie vraiment la façon dont cette question a été posée et a été mise à jour depuis. Elle décrit complètement ce à quoi je dois m'attendre dans les réponses. Je vais suivre cette approche dans mes questions à partir de maintenant. À la vôtre !

484voto

Kip Points 37013

Cette solution est écrite en PHP, mais elle devrait être facilement adaptable à d'autres langages.

L'original .htaccess regex peut causer des problèmes avec des fichiers comme json-1.3.js . La solution consiste à ne réécrire que s'il y a exactement 10 chiffres à la fin. (Parce que 10 chiffres couvrent tous les horodatages du 9/9/2001 au 20/11/2286).

Tout d'abord, nous utilisons la règle de réécriture suivante dans le fichier .htaccess :

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

Maintenant, nous écrivons la fonction PHP suivante :

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

Maintenant, où que vous incluiez votre CSS, changez-le à partir de ceci :

<link rel="stylesheet" href="http://stackoverflow.com/css/base.css" type="text/css" />

A ceci :

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

De cette façon, vous ne devez plus jamais modifier la balise de lien, et l'utilisateur verra toujours le dernier CSS. Le navigateur pourra mettre le fichier CSS en cache, mais lorsque vous apporterez des modifications à votre CSS, le navigateur le verra comme une nouvelle URL et n'utilisera pas la copie en cache.

Cela peut également fonctionner avec des images, des favicons et des JavaScript. En fait, tout ce qui n'est pas généré dynamiquement.

17 votes

Mon propre serveur de contenu statique fait exactement la même chose, sauf que j'utilise un paramètre pour le versioning (base.css?v=1221534296) plutôt qu'un changement de nom de fichier (base.1221534296.css). Je pense que votre méthode est un peu plus efficace. Très cool.

0 votes

Excellente réponse. Je me demande si cela peut être utilisé lorsque les fichiers statiques sont dans d'autres serveurs. N'est-ce pas une bonne pratique que de stocker les fichiers statiques sur d'autres serveurs ?

4 votes

@Kip : Une solution très astucieuse. La réécriture d'URL a manifestement beaucoup plus à offrir que de simplement embellir les urls.

221voto

keparo Points 13747

Technique simple côté client

En général, la mise en cache est une bonne chose... Il existe donc deux techniques, selon que vous corrigez le problème pour vous-même en développant un site Web, ou que vous essayez de contrôler le cache dans un environnement de production.

Les visiteurs généraux de votre site Web ne vivront pas la même expérience que vous lorsque vous le développez. Étant donné que le visiteur moyen vient moins souvent sur le site (peut-être seulement quelques fois par mois, à moins que vous ne soyez un Google ou un Hi5 Networks), il est moins probable qu'il ait vos fichiers en cache, et cela peut suffire.

Si vous voulez imposer une nouvelle version au navigateur, vous pouvez toujours ajouter une chaîne de requête à la demande et augmenter le numéro de version lorsque vous apportez des modifications importantes :

<script src="/myJavascript.js?version=4"></script>

Cela permettra à tout le monde de recevoir le nouveau fichier. Cela fonctionne parce que le navigateur examine l'URL du fichier pour déterminer s'il en a une copie dans le cache. Si votre serveur n'est pas configuré pour faire quoi que ce soit avec la chaîne de requête, elle sera ignorée, mais le nom ressemblera à un nouveau fichier pour le navigateur.

D'un autre côté, si vous développez un site web, vous ne voulez pas changer le numéro de version à chaque fois que vous enregistrez une modification de votre version de développement. Ce serait fastidieux.

Ainsi, pendant que vous développez votre site, une bonne astuce serait de générer automatiquement un paramètre de chaîne de requête :

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

L'ajout d'une chaîne de requête à la demande est un bon moyen de versionner une ressource, mais pour un site web simple, cela peut être inutile. Et n'oubliez pas que la mise en cache est une bonne chose.

Il convient également de noter que le navigateur n'est pas nécessairement avare de fichiers dans le cache. Les navigateurs ont des politiques pour ce genre de choses, et ils respectent généralement les règles définies dans la spécification HTTP. Lorsqu'un navigateur envoie une requête à un serveur, une partie de la réponse est un fichier de type Expiration : ... une date qui indique au navigateur combien de temps il doit rester dans le cache. La prochaine fois que le navigateur rencontre une demande pour le même fichier, il voit qu'il en a une copie dans le cache et se tourne vers l'en-tête Expiration : pour décider s'il faut l'utiliser.

Croyez-le ou non, c'est en fait votre serveur qui rend le cache du navigateur si persistant. Vous pouvez ajuster les paramètres de votre serveur et changer le Expiration : mais la petite technique que j'ai écrite ci-dessus est probablement une façon beaucoup plus simple pour vous de procéder. Puisque la mise en cache est une bonne chose, vous voulez généralement fixer cette date loin dans le futur (un "Far-future Expires Header"), et utiliser la technique décrite ci-dessus pour forcer un changement.

Si vous souhaitez obtenir plus d'informations sur HTTP ou sur la manière dont ces requêtes sont effectuées, un bon livre est "High Performance Web Sites" de Steve Souders. Il s'agit d'une très bonne introduction au sujet.

3 votes

L'astuce rapide consistant à générer une chaîne de requête avec Javascript fonctionne très bien pendant le développement actif. J'ai fait la même chose avec PHP.

2 votes

C'est le moyen le plus simple d'obtenir le résultat souhaité par l'auteur de l'affiche originale. La méthode mod_rewrite fonctionne bien si vous voulez forcer le rechargement du fichier .css ou .js CHAQUE fois que vous chargez la page. Cette méthode permet toujours la mise en cache jusqu'à ce que vous modifiiez le fichier et que vous vouliez vraiment forcer le rechargement.

0 votes

@keparo, j'ai un grand nombre de jquery dans toutes les pages, si je dois les changer manuellement, cela prendra un mois. Si vous pouvez m'aider à résoudre tout cela sans coder chaque page.

115voto

Leopd Points 12652

Le site de Google mod_pagespeed plugin pour Apache fera l'auto-version pour vous. C'est très pratique.

Il analyse le HTML à sa sortie du serveur web (fonctionne avec PHP, Ruby on Rails Python, HTML statique - n'importe quoi) et réécrit les liens vers les fichiers CSS, JavaScript, images pour qu'ils incluent un code d'identification. Il sert les fichiers aux URL modifiées avec un contrôle de cache très long sur eux. Lorsque les fichiers changent, il modifie automatiquement les URL pour que le navigateur doive les récupérer à nouveau. En fait, cela fonctionne simplement, sans aucune modification de votre code. Il va même réduire votre code en cours de route.

1 votes

C'est génial, mais toujours en version bêta. Peut-on l'utiliser pour un service d'entreprise ?

27 votes

Ce n'est pas bien (auto-fiddling avec la source) quand c'est clairement un problème de navigateur. Donnez-nous (développeurs) un vrai rafraîchissement de cerveau : <ctrl>+F5

26 votes

Mod_pagespeed est fonctionnellement équivalent à une étape de construction/compilation complètement automatique pour votre html/css/js. Je pense que vous aurez du mal à trouver des développeurs sérieux qui pensent que les systèmes de compilation sont intrinsèquement mauvais, ou qu'il y a quelque chose de mal à ce qu'ils soient complètement automatiques. L'analogie d'une construction propre est de vider le cache de mod_pagespeed : code.google.com/p/modpagespeed/wiki/ ?

97voto

levik Points 22462

Au lieu de modifier la version manuellement, je vous recommande d'utiliser un hachage MD5 du fichier CSS réel.

Votre URL serait donc quelque chose comme

http://mysite.com/css/[md5_hash_here]/style.css

Vous pouvez toujours utiliser la règle de réécriture pour supprimer le hachage, mais l'avantage est que vous pouvez maintenant définir votre politique de mise en cache sur "mise en cache permanente", puisque si l'URL est la même, cela signifie que le fichier est inchangé.

Vous pouvez ensuite écrire un simple script shell script qui calculerait le hachage du fichier et mettrait à jour votre balise (vous voudrez probablement la déplacer dans un fichier séparé pour l'inclusion).

Il suffit d'exécuter ce script à chaque fois que le CSS change et vous êtes bon. Le navigateur ne rechargera vos fichiers QUE lorsqu'ils seront modifiés. Si vous effectuez une modification puis l'annulez, vous n'aurez pas à vous soucier de savoir à quelle version vous devez revenir pour que vos visiteurs ne rechargent pas.

1 votes

Malheureusement, je ne sais pas comment le mettre en œuvre. Veuillez me conseiller ...plus de détails...

0 votes

Une implémentation en shell, ruby, etc. serait formidable.

3 votes

Très bonne solution mais je pense qu'il faut beaucoup de ressources pour calculer le hash du fichier dans chaque requête de fichier (css, js, images, html..etc) pour chaque visite de page.

57voto

SCdF Points 11397

Vous pouvez simplement mettre ?foo=1234 à la fin de votre importation CSS / JavaScript, en remplaçant 1234 par ce que vous voulez. Jetez un coup d'œil à la source HTML de Stack Overflow pour un exemple.

L'idée étant que le ? sont de toute façon rejetés ou ignorés lors de la demande. Vous pouvez modifier ce nombre lorsque vous lancez une nouvelle version.


Nota: La manière exacte dont cela affecte la mise en cache fait l'objet de discussions. Je crois que l'idée générale est que GET demandes, avec ou sans paramètres devrait être cachable, donc la solution ci-dessus devrait fonctionner.

Cependant, c'est à la fois au serveur web de décider s'il veut adhérer à cette partie de la spécification et au navigateur utilisé par l'utilisateur, car il peut de toute façon demander une nouvelle version.

0 votes

C'est absurde. La chaîne de requête (alias paramètres GET) fait partie de l'URL. Ils peuvent être et seront mis en cache. C'est une bonne solution.

9 votes

@troelskn : La spécification HTTP 1.1 dit le contraire (en ce qui concerne les requêtes GET et HEAD avec des paramètres d'interrogation) : les caches NE DOIVENT PAS traiter les réponses à ces URI comme fraîches, sauf si le serveur fournit un délai d'expiration explicite. Voir w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9

4 votes

J'ai essayé le type de versionnement par chaîne de requête avec tous les principaux navigateurs et ils mettent effectivement le fichier en cache, qu'il s'agisse de spécifications ou non. Cependant, je pense qu'il est préférable d'utiliser le format style.TIMESTAMP.css sans abuser des chaînes d'interrogation de toute façon parce qu'il y a toujours la possibilité que le logiciel proxy de mise en cache ne mette PAS le fichier en cache.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X