Faegy
Faegy

Reputation: 992

How to cache dynamically created images?

I have a php script that generates *.png pictures but does not store them on the server. Those pictures will be shown to a same user repeatedly. I'm looking for a way to cache those pictures. I'm able to send the 304 status header, the browser also declares the webpage as cached at first refresh (as it is visible in the web-inspector) but the webpage is shown blank. After a second refresh even the web-inspector shows blank.

Can someone help me and tell where I messed up?

Notes:

Here are the main lines of the file:

session_start();
header("Content-type: image/jpeg");
//A little require_once() here on some functions stored in an other file.
$originalSource = getSource($_GET['src']);
if (isset($_COOKIE[sha1($originalSource)]) && $_COOKIE[sha1($originalSource)]) {
    header("HTTP/1.1 304 Not Modified");
    die;
} else {
    setcookie (sha1($originalSource), true, time()+10);
    $offset = 10;
    $expire = "Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";
    header($expire);
    header("Cache-Control: max-age=".$offset);
    header("Last-Modified: Wed, 25 Feb 2015 12:00:00 GMT");
}
//The entire image generation process after this

Thanks for your help 💛

Upvotes: 1

Views: 1215

Answers (1)

deceze
deceze

Reputation: 522016

Your server is the one deciding here whether it thinks the browser should still have the image cached or not. Obviously it is hardly the authority on that. There are any number of reasons why the browser may not have the image cached, e.g. if you open the inspector tools (which typically disables caching).

The browser has a mechanism for informing the server about its cache status: the HTTP headers If-None-Match for ETags and If-Modified-Since for time-based expiry. If any of these two headers is present in the request, that means the browser still has a copy of the resourced cached and would happily accept a 304 response instead of downloading the resource again.

If you set an ETag header in your response, the browser will do another request using If-None-Match (that essentially replaces your cookie mechanism, more reliably); if you just set an expiration date, the browser will check again with the server using the If-Modified-Since header. That's what you should base your 304 reply on.

Example using ETags:

$hash = sha1($originalSource);

header("Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT");

if (
    isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
    trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $hash
) {
    header("HTTP/1.1 304 Not Modified");
    exit;
}

header("Content-type: image/jpeg");
header("Cache-Control: max-age=$offset");
header("ETag: \"$hash\"");

// output image

Upvotes: 2

Related Questions