How to get the browser to cache images, with PHP?

50,413

Solution 1

If you are using php to check if the user is logged in before outputting the message, then you don't want the browser to cache the image.

The entire point of caching is to call the server once and then never call it again. If the browser caches the image, it won't call the server and your script won't run. Instead, the browser will pull your image from cache and display it, even if the user is no longer logged in. This could potentially be a very big security hole.

Solution 2

First of all, if you're using sessions, you must disable session_cache_limiter (by setting it to none or public). Headers it sends are pretty bad for caches.

session_cache_limiter('none');

Then send Cache-Control: max-age=number_of_seconds and optionally an equivalent Expires: header.

header('Cache-control: max-age='.(60*60*24*365));
header('Expires: '.gmdate(DATE_RFC1123,time()+60*60*24*365));

For the best cacheability, send Last-Modified header and reply with status 304 and empty body if the browser sends a matching If-Modified-Since header.

header('Last-Modified: '.gmdate(DATE_RFC1123,filemtime($path_to_image)));

For brevity I'm cheating here a bit (the example doesn't verify the date), but it's valid as long as you don't mind browsers keeping the cached file forever:

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
   header('HTTP/1.1 304 Not Modified');
   die();
}

Solution 3

Here's some code I use for 304 header support:

  /**
   * @return false if not cached or modified, true otherwise.
   * @param bool check_request set this to true if you want to check the client's request headers and "return" 304 if it makes sense. will only output the cache response headers otherwise.
   **/     
  protected function sendHTTPCacheHeaders($cache_file_name, $check_request = false)
  {
    $mtime = @filemtime($cache_file_name);

    if($mtime > 0)
    {
      $gmt_mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
      $etag = sprintf('%08x-%08x', crc32($cache_file_name), $mtime);

      header('ETag: "' . $etag . '"');
      header('Last-Modified: ' . $gmt_mtime);
      header('Cache-Control: private');
      // we don't send an "Expires:" header to make clients/browsers use if-modified-since and/or if-none-match

      if($check_request)
      {
        if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && !empty($_SERVER['HTTP_IF_NONE_MATCH']))
        {
          $tmp = explode(';', $_SERVER['HTTP_IF_NONE_MATCH']); // IE fix!
          if(!empty($tmp[0]) && strtotime($tmp[0]) == strtotime($gmt_mtime))
          {
            header('HTTP/1.1 304 Not Modified');
            return false;
          }
        }

        if(isset($_SERVER['HTTP_IF_NONE_MATCH']))
        {
          if(str_replace(array('\"', '"'), '', $_SERVER['HTTP_IF_NONE_MATCH']) == $etag)
          {
            header('HTTP/1.1 304 Not Modified');
            return false;
          }
        }
      }
    }

    return true;
  }

Solution 4

You could store the generated images in a directory called "showImage" so that you would embed them like this

<img src="showimage/601.jpg" />

Then you place a .htaccess file in the very same directory that will call showImage.php?id= in case the file does not exist, e.g.:

<IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteRule ^(.*)\.jpg$ showImage.php?id=$1 [QSA,L]
</IfModule>

Just read in your comment that you want to do client side caching: just set the caching-related HTTP headers according to http://www.mnot.net/cache_docs/

Solution 5

Please do not address images are some id'ed resource. use absolute urls for images, preferably in a subdomain, preferably cookie less. The browser will do the caching on the images. A neat trick to load images faster on websites is to put it on some CDN or other site. This because browsers limit the number of parallel request threads to one domain.

Another neat way of working with images is spriting, look it up. It saves a lot of bandwidth and also requests.

You could also use direct bitmap loading if speed is so crucial. This is not advised for large images though. If its icons and small images/gifs that you are loading. You can use bitmaps directly on the page.

Share:
50,413
Johan
Author by

Johan

Updated on July 25, 2022

Comments

  • Johan
    Johan almost 2 years

    I'm totally new to how to cache images.

    I output all images in a gallery with PHP, and want the images already shown, to be cached by the browser, so the PHP script don't have to output the same image again. All I want is the images to show up faster.

    When calling an image I do like this:

    <img src="showImage.php?id=601">
    

    and the showImage.php-file does:

    $id = (int) $_GET['id'];
    $resultat = mysql_query("
        SELECT filename, id
        FROM Media 
        WHERE id = $id
    ");
    $data = mysql_fetch_assoc($resultat);
    
    ...
    
    //Only if the user are logged in
    if(isset($_SESSION['user'])){
        header("Content-Type: image/jpeg");
    
        //$data['filename'] can be = dsSGKLMsgKkD3325J.jpg
        echo(file_get_contents("images/".$data['filename']."")); 
    }