Laravel Headers and Caching in php

carbontwelve picture carbontwelve · Aug 7, 2013 · Viewed 9.2k times · Source

I have a small image generator as part of my laravel4 application. It takes about 700ms to generate the image and so I have started caching the generated result on my server and returning that to the browser instead which saves some time.

As the image will never change once generated I wanted to tell the browser to cache the image locally and I have done this with the following code:

$path = $cacheFolderPath . $cacheFileName;

if (File::exists( $path )){
    $response = Response::make(File::get($path));
    $response->header('Content-Type', 'image/png');
    $response->header('Content-Disposition', 'inline; filename="'.$cacheFileName.'"');
    $response->header('Content-Transfer-Encoding', 'binary');
    $response->header('Cache-Control', 'public, max-age=10800, pre-check=10800');
    $response->header('Pragma', 'public');
    $response->header('Expires', date(DATE_RFC822,strtotime(" 2 day")) );
    $response->header('Last-Modified', date(DATE_RFC822, File::lastModified($path)) );
    $response->header('Content-Length', filesize($path));
    return $response;
}

This sends an image with status code 200 OK to the browser with the following headers:

Cache-Control:max-age=10800, pre-check=10800, public
Connection:Keep-Alive
Content-Disposition:inline; filename="pie_0_normal.png"
Content-Length:2129
Content-Transfer-Encoding:binary
Content-Type:image/png
Date:Wed, 07 Aug 2013 10:29:20 GMT
Expires:Fri, 09 Aug 13 10:29:20 +0000
Keep-Alive:timeout=5, max=93
Last-Modified:Wed, 07 Aug 13 10:14:42 +0000
Pragma:public
Server:Apache/2.4.3 (Win32) OpenSSL/1.0.1c PHP/5.4.7
Set-Cookie:laravel_session=767487mhf6j2btv3k01vu56174; expires=Wed, 07-Aug-2013 12:29:20 GMT; path=/; httponly
X-Powered-By:PHP/5.4.7

My issue is that my browser (chrome, not tested in others) still refuses to simply grab the local cached version and instead hits the server again.

I have spent about half an hour searching for other questions on this subject and all of them have given me answers which I have incorporated into the above code. So while I know that there are similar questions, this one is unique to the above source code.

My question is, what am I doing wrong that would result in the file not being cached by the browser?

Answer

darronz picture darronz · Aug 10, 2013

An alternative method to this would be to check for the 'If-Modified-Since' request header as it will only be present if the browser already has the file.

If it is present, then you know the file is already created and can respond with a link to it, otherwise run your code above. Something like this...

// check if the client validating cache and if it is current
if ( isset( $headers['If-Modified-Since'] ) && ( strtotime( $headers['If-Modified-Since'] ) == filemtime( $image->get_full_path() ) ) ) {

    // cache IS current, respond 304
    header( 'Last-Modified: ' . $image->get_last_modified(), true, 304 );

} else {

    // not cached or client cache is older than server, respond 200 and output

    header( 'Last-Modified: ' . $image->get_last_modified(), true, 200 );
    header( 'Content-Length: ' . $image->get_filesize() );
    header( 'Cache-Control: max-age=' . $image->get_expires() );
    header( 'Expires: '. gmdate('D, d M Y H:i:s \G\M\T', time() + $image->get_expires() ) );
    header( 'Content-Type: image/jpeg');

    print file_get_contents( $image->get_full_path() ); 
}