Make browsers cache your static files with PHP

Namecheap AuctionsNamecheap Auctions
Namecheap AuctionsNamecheap Auctions
SpaceshipSpaceship
Watch

Hobnob

Established Member
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
Close
The views expressed on this page by users and staff are their own, not those of NamePros.
GoDaddyGoDaddy
Thanks for sharing:) Nice code:tu:
 
0
Close
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
Close
...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
Close
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
Close
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
Close
What files get cached by major browsers without any coding?
.jpg/.gif/.png = Yes (correct?)
.ico Yes
.js & .css ??
 
0
Close
DomainEasy: white label portfolios are live. Enable yours.

We're social

Escrow.com
Spaceship
Domain Recover
CryptoExchange.com

NamePros updates

Switch between accounts
You can now stay logged in to more than one NamePros account in the same browser. Click your avatar, then "Accounts in this browser", to...
AIVikings
Catchy
  • The sidebar remains visible by scrolling at a speed relative to the page’s height.
Back