IT.COM

Make browsers cache your static files with PHP

Spaceship Spaceship
Watch
Impact
5
Hey, I had an issue where I needed to make browsers cache my static content (images, css, javascript, etc.) to reduce the server load. However due to the incompetent host I was unable to use Apache's EXPIRES_MODULE, so I came up with this solution:

Step 1: Create the following directory structure: (static/images, static/css, static/javascript) that is publicly accessible; i.e. http://www.example.com/static/images/xample.jpg

Step 2: Add the following to your .htaccess:
Code:
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(images|css|javascript)/(.*)$ static/static.php?requested_file=$1/$2 [L]

Step 3: Create a static.php file in your "static" directory (http://www.example.com/static/static.php) with this code:
PHP:
<?php
/*
 * The aim of this file is to handle static files {images, javascript, css,
 * html, etc.} by setting file expiration headers so that the browsers cache the
 * files instead of requesting them again and again on each request.
 * 
 *       **** THIS IS TO BYPASS THE NEED FOR THE APACHE EXPIRES_MODULE ****
 */

$requested_file = $_GET["requested_file"];


/*
 * Make sure a filename was passed and that it exists on the server.
 */
isset($requested_file) or die("Invalid file: {$requested_file}");
is_file($requested_file) or die("The file '{$requested_file}' does not exist.");


/*
 * The accepted mime types
 */
$mime_map = array('bm' => 'image/bmp', 'bmp' => 'image/bmp',
    'css' => 'text/css', 'gif' => 'image/gif', 'jpeg' => 'image/jpeg',
    'jpg' => 'image/jpeg', 'mjpg' => 'video/x-motion-jpeg', 'xml' => 'text/xml',
    'png' => 'image/png');


/*
 *
 */
class File {
    var $filepath, $mtime;
    var $dirname, $name, $extension;

    function __construct($file) {
        $this->filepath = $file;
        $this->mtime = filemtime($this);

        $path_parts = pathinfo($this);
        $this->dirname   = $path_parts['dirname'];
        $this->name      = $path_parts['basename'];
        $this->extension = strtolower($path_parts['extension']);
    }

    function get_etag() {
        return md5($this->mtime.$this);
    }

    function content() {
        ob_start();
        readfile($this);
        $content = ob_get_contents();
        ob_end_clean();
        return $content;
    }

    function __toString() {
        return (string)$this->filepath;
    }
}

$file = new File($requested_file);


/*
 * Make sure the file type is supported
 */
array_key_exists($file->extension, $mime_map)
        or die("Invalid file type: $file->extension");


/*
 * Caching headers
 */
$etag = $file->get_etag();
$time = gmdate('r', $file->mtime);

$test1 = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
    && $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $time;

$test2 = isset($_SERVER['HTTP_IF_NONE_MATCH'])
    && str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) == $etag;

if($test1 || $test2){
    header('HTTP/1.1 304 Not Modified');
    exit();
}

header("Last-Modified: $time");
header("Cache-Control: must-revalidate");
header("Expires: $time");
header("Etag: $etag");
header("Content-type: ".$mime_map[$file->extension]);

echo $file->content();

?>

Step 4: Call your static content like so: http://www.example.com/images/myimage.jpg, http://www.example.com/css/mystylesheet.css, http://www.example.com/javascript/myscript.js, http://www.example.com/images/subdir/myimage2.jpg

* note this was just a quick and dirty script that has not been optimized in any way, it was tested with the latest version of Firefox and IE. If you see any issues let me know
 
Last edited:
1
•••
The views expressed on this page by users and staff are their own, not those of NamePros.
Thanks for sharing:) Nice code:tu:
 
0
•••
Some of the best stuff here seems to get the least eyeballs and responses... once again, great tip, and thanks for sharing :) GoDaddy doesn't smile on mod_rewrite, but it can handle this!
 
0
•••
...or just use:

Code:
<filesMatch "\.(ico|gif|jpg|jpeg|png|flv|avi|mov|mp4|mpeg|mpg|mp3|pdf|doc|css|js|html|bmp|js|css)$">
  Header set Cache-Control "max-age=86400"
</filesMatch>

in your httpd.conf include file or .htaccess. This tells the browser to cache files.

Your code would waste too much loading time. Good thinking, but needs work.
 
0
•••
Some people don't have the ability to use the Header set (like the customer I wrote that for). As far as the loading time, it's nothing significant and it's less of a load than not caching...

But I'm glad you posted that, it's probably easier for most people.
 
0
•••
Actually, it works perfect with Chrome, IE7, IE8 and FF3 out of the box and this spead up the loading of Glamourislife.com from 3.6 seconds to 0.13 secnds on a 20 Mb/s connection. For sites using worpress or anything else witha ton of included files, this is vital.

Code:
<FilesMatch "\.(ico|gif|jpg|jpeg|png|flv|avi|mov|mp4|mpeg|mpg|mp3|pdf|bmp|js|css|flv|swf|doc)$">
Header set Cache-Control "max-age=7200"
</FilesMatch>

<FilesMatch "\.(xml|txt)$">
Header set Cache-Control "max-age=172800, public, must-revalidate"
</FilesMatch>
 
<FilesMatch "\.(html|htm)$">
Header set Cache-Control "max-age=7200, must-revalidate"
</FilesMatch>
 
0
•••
What files get cached by major browsers without any coding?
.jpg/.gif/.png = Yes (correct?)
.ico Yes
.js & .css ??
 
0
•••
  • The sidebar remains visible by scrolling at a speed relative to the page’s height.
Back