web-dev-qa-db-fra.com

Comment utiliser les en-têtes de cache HTTP avec PHP

J'ai un PHP 5.1.0 (en fait c'est 5.2.9 mais il doit également fonctionner sur 5.1.0+).

Les pages sont générées dynamiquement mais beaucoup d'entre elles sont pour la plupart statiques. Par statique, je veux dire que le contenu ne change pas, mais le "modèle" autour du contenu peut changer au fil du temps.

Je sais qu'il existe plusieurs systèmes de cache et des cadres PHP déjà disponibles, mais mon hôte n'a pas installé APC ou Memcached et je n'utilise aucun cadre pour ce projet particulier.

Je veux que les pages soient mises en cache (je pense que par défaut PHP "interdire" le cache). Jusqu'à présent, j'utilise:

session_cache_limiter('private'); //Aim at 'public'
session_cache_expire(180);
header("Content-type: $documentMimeType; charset=$documentCharset");
header('Vary: Accept');
header("Content-language: $currentLanguage");

J'ai lu de nombreux tutoriels mais je ne trouve pas quelque chose de simple (je sais que le cache est quelque chose de complexe, mais j'ai seulement besoin de quelques trucs de base).

Quels sont les "en-têtes" à envoyer pour aider à la mise en cache?

59
AlexV

Vous voudrez peut-être utiliser private_no_expire au lieu de private, mais définissez une longue expiration pour le contenu que vous savez que cela ne changera pas et assurez-vous de traiter if-modified-since et if-none-match requêtes similaires au post d'Emil.

$tsstring = gmdate('D, d M Y H:i:s ', $timestamp) . 'GMT';
$etag = $language . $timestamp;

$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
if ((($if_none_match && $if_none_match == $etag) || (!$if_none_match)) &&
    ($if_modified_since && $if_modified_since == $tsstring))
{
    header('HTTP/1.1 304 Not Modified');
    exit();
}
else
{
    header("Last-Modified: $tsstring");
    header("ETag: \"{$etag}\"");
}

$etag pourrait être une somme de contrôle basée sur le contenu ou l'ID utilisateur, la langue et l'horodatage, par exemple.

$etag = md5($language . $timestamp);
45
Steve-o

Vous devez avoir un en-tête Expires. Techniquement, il existe d'autres solutions, mais l'en-tête Expires est vraiment le meilleur, car il indique au navigateur de ne pas revérifier la page avant la date et l'heure d'expiration et de simplement servir le contenu du cache. Ça marche vraiment super!

Il est également utile de rechercher un en-tête If-Modified-Since dans la demande du navigateur. Cet en-tête est envoyé lorsque le navigateur n'est pas sûr que le contenu de son cache soit toujours la bonne version. Si votre page n'a pas été modifiée depuis ce temps, renvoyez simplement un code HTTP 304 (non modifié). Voici un exemple qui envoie un code 304 pendant dix minutes:

<?php
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  if(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < time() - 600) {
    header('HTTP/1.1 304 Not Modified');
    exit;
  }
}
?>

Vous pouvez placer cette vérification au début de votre code pour économiser les ressources du serveur.

12
Emil Vikström
<?php
header("Expires: Sat, 26 Jul 2020 05:00:00 GMT"); // Date in the future
?>

La définition d'une date d'expiration pour la page mise en cache est un moyen utile de la mettre en cache côté client.

8
S Pangborn

Faites votre choix - ou utilisez-les tous! :-)

 en-tête ('Expire: jeu, 01-jan-70 00:00:01 GMT'); 
 en-tête ('Dernière modification:'. gmdate ('D, d MYH: i: s ').' GMT '); en-tête 
 (' Cache-Control: no-store, no-cache, must-revalidate '); 
 header (' Cache-Control: post-check = 0, pré-vérification = 0 ', faux); en-tête 
 (' Pragma: no-cache '); 
7
Mike Foster

Voici une petite classe qui fait la mise en cache http pour vous. Il a une fonction statique appelée 'Init' qui nécessite 2 paramètres, un horodatage de la date à laquelle la page (ou tout autre fichier demandé par le navigateur) a été modifiée pour la dernière fois et l'âge maximum, en secondes, dans lequel cette page peut être conservée. cache par le navigateur.

class HttpCache 
{
    public static function Init($lastModifiedTimestamp, $maxAge)
    {
        if (self::IsModifiedSince($lastModifiedTimestamp))
        {
            self::SetLastModifiedHeader($lastModifiedTimestamp, $maxAge);
        }
        else 
        {
            self::SetNotModifiedHeader($maxAge);
        }
    }

    private static function IsModifiedSince($lastModifiedTimestamp)
    {
        $allHeaders = getallheaders();

        if (array_key_exists("If-Modified-Since", $allHeaders))
        {
            $gmtSinceDate = $allHeaders["If-Modified-Since"];
            $sinceTimestamp = strtotime($gmtSinceDate);

            // Can the browser get it from the cache?
            if ($sinceTimestamp != false && $lastModifiedTimestamp <= $sinceTimestamp)
            {
                return false;
            }
        }

        return true;
    }

    private static function SetNotModifiedHeader($maxAge)
    {
        // Set headers
        header("HTTP/1.1 304 Not Modified", true);
        header("Cache-Control: public, max-age=$maxAge", true);
        die();
    }

    private static function SetLastModifiedHeader($lastModifiedTimestamp, $maxAge)
    {
        // Fetching the last modified time of the XML file
        $date = gmdate("D, j M Y H:i:s", $lastModifiedTimestamp)." GMT";

        // Set headers
        header("HTTP/1.1 200 OK", true);
        header("Cache-Control: public, max-age=$maxAge", true);
        header("Last-Modified: $date", true);
    }
}
7
Jasper

Je faisais la mise en cache JSON sur le serveur provenant du flux Facebook, rien ne fonctionnait jusqu'à ce que je mette à jour et masquais les rapports d'erreur. Je sais que ce n'est pas du code idéal, mais je voulais une solution rapide.

error_reporting(0);
    $headers = Apache_request_headers();
    //print_r($headers);
    $timestamp = time();
    $tsstring = gmdate('D, d M Y H:i:s ', $timestamp) . 'GMT';
    $etag = md5($timestamp);
    header("Last-Modified: $tsstring");
    header("ETag: \"{$etag}\"");
    header('Expires: Thu, 01-Jan-70 00:00:01 GMT');

    if(isset($headers['If-Modified-Since'])) {
            //echo 'set modified header';
            if(intval(time()) - intval(strtotime($headers['IF-MODIFIED-SINCE'])) < 300) {
              header('HTTP/1.1 304 Not Modified');
              exit();
            }
    }
    flush();
//JSON OP HERE

Cela a très bien fonctionné.

2
abksharma

C'est la meilleure solution pour le cache php. Utilisez-la simplement en haut du script

$seconds_to_cache = 3600;
$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";
header("Expires: $ts");
header("Pragma: cache");
header("Cache-Control: max-age=$seconds_to_cache");
0
Amit Ghosh Anto