Forcer la mise en cache d'images retournées depuis un script

Il peut arriver que l'on passe par un programme pour retourner une image sur une page web, avec ou sans URL rewriting pour ça.

En général c'est pour des images stockées dans une arborescence qu'on ne veut pas montrer aux visiteurs des sites ou depuis une base de données (beurk pour les stocker, mais c'est un autre débat).

Par défaut, tout ce qui est dynamique est sans mise en cache. Les serveurs font le nécessaire car ils traitent du code. C'est donc à nous de nous en préoccuper.

Après quelques essais, j'en suis arrivé à cette combinaison qui donne de bons résultats sur les navigateurs sur lesquels j'ai vérifié :

	$UnAnEnSecondes = 60*60*24*365;
	header("Content-type: image/jpeg");
	header("Cache-Control: public, max-age=".$UnAnEnSecondes);
	header("Expires: ".gmdate('D, d M Y H:i:s \G\M\T', time()+$UnAnEnSecondes));
	header("Last-Modified: ".gmdate('D, d M Y H:i:s \G\M\T', filemtime($path_photo)));
	header("Content-Length: ".filesize($path_photo));
	@readfile($path_photo);

Dans le cas de ce site je mets tout en cache pour 1 an et j'autorise une mise en cache partagé par les utilisateurs du même ordinateur. Dans certains cas passez plutôt par "private" que "public".

Les visiteurs venant régulièrement dessus et les images n'évoluant pas, autant limiter le trafic réseau inutile et gagner du CPU côté serveur. 1 an est un bon compromis (même si ça peut faire beaucoup). Notez quand même que si vous vous loupez, le fichier ne sera jamais rechargé avant son expiration, donc faites gaffe !

Ici je retourne des images en format JPG, donc le Content-Type est adapté. Bien entendu il faudra mettre le type MIME adapté au fichier retourné et au traitement que vous désirez forcer côté navigateur.

J'utilise la date de dernière modification du fichier pour en fournir le Last-Modified (ce qui me semble plutôt bien trouvé, merci pour les fleurs). La longueur du fichier n'est pas une obligation, mais ça peut aider certains logiciels à estimer un temps de chargement selon le type de contenu, alors autant la donner vu que ça ne nous coûte pas grand chose à mettre.

Enfin, je retourne le contenu du fichier avec la fonction readfile() de PHP qui lit un fichier en l'envoyant sur la sortie standard.

Avec ça, vous devriez gagner de la bande passante et sauver des arbres (puisque d'après l'assemblée nationale française ça a un lien...) !


Petit ajout : selon la configuration de votre serveur ou l'ancienneté de votre version de PHP, surtout si vous utilisez des sessions, il est possible qu'un "Pragma: no-cache" soit envoyé automatiquement.

Pour le supprimer ajoutez la ligne suivante en tête de votre programme (avant qu'un session_start() ait lieu). Elle évitera que la session retourne des infos de péremption de page.

session_cache_limiter("");

Bien entendu ne le faites que sur des fichiers qui doivent être mis en cache, jamais sur vos générations de pages ou API (sauf si vous désirez que ça reste bloqué quelque part sans possibilité de forcer un refresh pendant la durée du cache)...